From 6eea242f9bcdf828bb56334d8ee5c7cb466e4bcd Mon Sep 17 00:00:00 2001 From: Petr Mladek Date: Wed, 17 Apr 2019 13:53:41 +0200 Subject: vsprintf: Shuffle restricted_pointer() This is just a preparation step for further changes. The patch does not change the code. Link: http://lkml.kernel.org/r/20190417115350.20479-2-pmladek@suse.com To: Rasmus Villemoes Cc: Linus Torvalds Cc: "Tobin C . Harding" Cc: Joe Perches Cc: Andrew Morton Cc: Michal Hocko Cc: Steven Rostedt Cc: Sergey Senozhatsky Cc: linux-kernel@vger.kernel.org Reviewed-by: Andy Shevchenko Reviewed-by: Sergey Senozhatsky Signed-off-by: Petr Mladek --- lib/vsprintf.c | 98 +++++++++++++++++++++++++++++----------------------------- 1 file changed, 49 insertions(+), 49 deletions(-) (limited to 'lib') diff --git a/lib/vsprintf.c b/lib/vsprintf.c index 791b6fa36905..eb7b4a06e1f0 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -717,6 +717,55 @@ static char *ptr_to_id(char *buf, char *end, const void *ptr, return pointer_string(buf, end, (const void *)hashval, spec); } +int kptr_restrict __read_mostly; + +static noinline_for_stack +char *restricted_pointer(char *buf, char *end, const void *ptr, + struct printf_spec spec) +{ + switch (kptr_restrict) { + case 0: + /* Always print %pK values */ + break; + case 1: { + const struct cred *cred; + + /* + * kptr_restrict==1 cannot be used in IRQ context + * because its test for CAP_SYSLOG would be meaningless. + */ + if (in_irq() || in_serving_softirq() || in_nmi()) { + if (spec.field_width == -1) + spec.field_width = 2 * sizeof(ptr); + return string(buf, end, "pK-error", spec); + } + + /* + * Only print the real pointer value if the current + * process has CAP_SYSLOG and is running with the + * same credentials it started with. This is because + * access to files is checked at open() time, but %pK + * checks permission at read() time. We don't want to + * leak pointer values if a binary opens a file using + * %pK and then elevates privileges before reading it. + */ + cred = current_cred(); + if (!has_capability_noaudit(current, CAP_SYSLOG) || + !uid_eq(cred->euid, cred->uid) || + !gid_eq(cred->egid, cred->gid)) + ptr = NULL; + break; + } + case 2: + default: + /* Always print 0's for %pK */ + ptr = NULL; + break; + } + + return pointer_string(buf, end, ptr, spec); +} + static noinline_for_stack char *dentry_name(char *buf, char *end, const struct dentry *d, struct printf_spec spec, const char *fmt) @@ -1476,55 +1525,6 @@ char *uuid_string(char *buf, char *end, const u8 *addr, return string(buf, end, uuid, spec); } -int kptr_restrict __read_mostly; - -static noinline_for_stack -char *restricted_pointer(char *buf, char *end, const void *ptr, - struct printf_spec spec) -{ - switch (kptr_restrict) { - case 0: - /* Always print %pK values */ - break; - case 1: { - const struct cred *cred; - - /* - * kptr_restrict==1 cannot be used in IRQ context - * because its test for CAP_SYSLOG would be meaningless. - */ - if (in_irq() || in_serving_softirq() || in_nmi()) { - if (spec.field_width == -1) - spec.field_width = 2 * sizeof(ptr); - return string(buf, end, "pK-error", spec); - } - - /* - * Only print the real pointer value if the current - * process has CAP_SYSLOG and is running with the - * same credentials it started with. This is because - * access to files is checked at open() time, but %pK - * checks permission at read() time. We don't want to - * leak pointer values if a binary opens a file using - * %pK and then elevates privileges before reading it. - */ - cred = current_cred(); - if (!has_capability_noaudit(current, CAP_SYSLOG) || - !uid_eq(cred->euid, cred->uid) || - !gid_eq(cred->egid, cred->gid)) - ptr = NULL; - break; - } - case 2: - default: - /* Always print 0's for %pK */ - ptr = NULL; - break; - } - - return pointer_string(buf, end, ptr, spec); -} - static noinline_for_stack char *netdev_bits(char *buf, char *end, const void *addr, struct printf_spec spec, const char *fmt) -- cgit From 1ac2f9789c4b76ad749870c25ffae0cbcd1f510f Mon Sep 17 00:00:00 2001 From: Petr Mladek Date: Wed, 17 Apr 2019 13:53:42 +0200 Subject: vsprintf: Consistent %pK handling for kptr_restrict == 0 restricted_pointer() pretends that it prints the address when kptr_restrict is set to zero. But it is never called in this situation. Instead, pointer() falls back to ptr_to_id() and hashes the pointer. This patch removes the potential confusion. klp_restrict is checked only in restricted_pointer(). It actually fixes a small race when the address might get printed unhashed: CPU0 CPU1 pointer() if (!kptr_restrict) /* for example set to 2 */ restricted_pointer() /* echo 0 >/proc/sys/kernel/kptr_restrict */ proc_dointvec_minmax_sysadmin() klpr_restrict = 0; switch(kptr_restrict) case 0: break: number() Fixes: ef0010a30935de4e0211 ("vsprintf: don't use 'restricted_pointer()' when not restricting") Link: http://lkml.kernel.org/r/20190417115350.20479-3-pmladek@suse.com To: Andy Shevchenko To: Rasmus Villemoes Cc: Linus Torvalds Cc: "Tobin C . Harding" Cc: Joe Perches Cc: Andrew Morton Cc: Michal Hocko Cc: Steven Rostedt Cc: Sergey Senozhatsky Cc: linux-kernel@vger.kernel.org Cc: Kees Cook Reviewed-by: Andy Shevchenko Reviewed-by: Steven Rostedt (VMware) Reviewed-by: Sergey Senozhatsky Signed-off-by: Petr Mladek --- lib/vsprintf.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'lib') diff --git a/lib/vsprintf.c b/lib/vsprintf.c index eb7b4a06e1f0..2af48948a973 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -725,8 +725,8 @@ char *restricted_pointer(char *buf, char *end, const void *ptr, { switch (kptr_restrict) { case 0: - /* Always print %pK values */ - break; + /* Handle as %p, hash and do _not_ leak addresses. */ + return ptr_to_id(buf, end, ptr, spec); case 1: { const struct cred *cred; @@ -2041,8 +2041,6 @@ char *pointer(const char *fmt, char *buf, char *end, void *ptr, return buf; } case 'K': - if (!kptr_restrict) - break; return restricted_pointer(buf, end, ptr, spec); case 'N': return netdev_bits(buf, end, ptr, spec, fmt); -- cgit From d529ac4194f2c346b2f62f0f473a578a7357039b Mon Sep 17 00:00:00 2001 From: Petr Mladek Date: Wed, 17 Apr 2019 13:53:43 +0200 Subject: vsprintf: Do not check address of well-known strings We are going to check the address using probe_kernel_address(). It will be more expensive and it does not make sense for well known address. This patch splits the string() function. The variant without the check is then used on locations that handle string constants or strings defined as local variables. This patch does not change the existing behavior. Link: http://lkml.kernel.org/r/20190417115350.20479-4-pmladek@suse.com To: Rasmus Villemoes Cc: Linus Torvalds Cc: "Tobin C . Harding" Cc: Joe Perches Cc: Andrew Morton Cc: Michal Hocko Cc: Steven Rostedt Cc: Sergey Senozhatsky Cc: linux-kernel@vger.kernel.org Reviewed-by: Andy Shevchenko Signed-off-by: Petr Mladek Reviewed-by: Sergey Senozhatsky --- lib/vsprintf.c | 81 +++++++++++++++++++++++++++++++--------------------------- 1 file changed, 44 insertions(+), 37 deletions(-) (limited to 'lib') diff --git a/lib/vsprintf.c b/lib/vsprintf.c index 2af48948a973..c9c9a1179870 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -593,15 +593,13 @@ char *widen_string(char *buf, int n, char *end, struct printf_spec spec) return buf; } -static noinline_for_stack -char *string(char *buf, char *end, const char *s, struct printf_spec spec) +/* Handle string from a well known address. */ +static char *string_nocheck(char *buf, char *end, const char *s, + struct printf_spec spec) { int len = 0; size_t lim = spec.precision; - if ((unsigned long)s < PAGE_SIZE) - s = "(null)"; - while (lim--) { char c = *s++; if (!c) @@ -615,6 +613,15 @@ char *string(char *buf, char *end, const char *s, struct printf_spec spec) } static noinline_for_stack +char *string(char *buf, char *end, const char *s, + struct printf_spec spec) +{ + if ((unsigned long)s < PAGE_SIZE) + s = "(null)"; + + return string_nocheck(buf, end, s, spec); +} + char *pointer_string(char *buf, char *end, const void *ptr, struct printf_spec spec) { @@ -701,7 +708,7 @@ static char *ptr_to_id(char *buf, char *end, const void *ptr, if (static_branch_unlikely(¬_filled_random_ptr_key)) { spec.field_width = 2 * sizeof(ptr); /* string length must be less than default_width */ - return string(buf, end, str, spec); + return string_nocheck(buf, end, str, spec); } #ifdef CONFIG_64BIT @@ -737,7 +744,7 @@ char *restricted_pointer(char *buf, char *end, const void *ptr, if (in_irq() || in_serving_softirq() || in_nmi()) { if (spec.field_width == -1) spec.field_width = 2 * sizeof(ptr); - return string(buf, end, "pK-error", spec); + return string_nocheck(buf, end, "pK-error", spec); } /* @@ -851,7 +858,7 @@ char *symbol_string(char *buf, char *end, void *ptr, else sprint_symbol_no_offset(sym, value); - return string(buf, end, sym, spec); + return string_nocheck(buf, end, sym, spec); #else return special_hex_number(buf, end, value, sizeof(void *)); #endif @@ -937,27 +944,27 @@ char *resource_string(char *buf, char *end, struct resource *res, *p++ = '['; if (res->flags & IORESOURCE_IO) { - p = string(p, pend, "io ", str_spec); + p = string_nocheck(p, pend, "io ", str_spec); specp = &io_spec; } else if (res->flags & IORESOURCE_MEM) { - p = string(p, pend, "mem ", str_spec); + p = string_nocheck(p, pend, "mem ", str_spec); specp = &mem_spec; } else if (res->flags & IORESOURCE_IRQ) { - p = string(p, pend, "irq ", str_spec); + p = string_nocheck(p, pend, "irq ", str_spec); specp = &default_dec_spec; } else if (res->flags & IORESOURCE_DMA) { - p = string(p, pend, "dma ", str_spec); + p = string_nocheck(p, pend, "dma ", str_spec); specp = &default_dec_spec; } else if (res->flags & IORESOURCE_BUS) { - p = string(p, pend, "bus ", str_spec); + p = string_nocheck(p, pend, "bus ", str_spec); specp = &bus_spec; } else { - p = string(p, pend, "??? ", str_spec); + p = string_nocheck(p, pend, "??? ", str_spec); specp = &mem_spec; decode = 0; } if (decode && res->flags & IORESOURCE_UNSET) { - p = string(p, pend, "size ", str_spec); + p = string_nocheck(p, pend, "size ", str_spec); p = number(p, pend, resource_size(res), *specp); } else { p = number(p, pend, res->start, *specp); @@ -968,21 +975,21 @@ char *resource_string(char *buf, char *end, struct resource *res, } if (decode) { if (res->flags & IORESOURCE_MEM_64) - p = string(p, pend, " 64bit", str_spec); + p = string_nocheck(p, pend, " 64bit", str_spec); if (res->flags & IORESOURCE_PREFETCH) - p = string(p, pend, " pref", str_spec); + p = string_nocheck(p, pend, " pref", str_spec); if (res->flags & IORESOURCE_WINDOW) - p = string(p, pend, " window", str_spec); + p = string_nocheck(p, pend, " window", str_spec); if (res->flags & IORESOURCE_DISABLED) - p = string(p, pend, " disabled", str_spec); + p = string_nocheck(p, pend, " disabled", str_spec); } else { - p = string(p, pend, " flags ", str_spec); + p = string_nocheck(p, pend, " flags ", str_spec); p = number(p, pend, res->flags, default_flag_spec); } *p++ = ']'; *p = '\0'; - return string(buf, end, sym, spec); + return string_nocheck(buf, end, sym, spec); } static noinline_for_stack @@ -1150,7 +1157,7 @@ char *mac_address_string(char *buf, char *end, u8 *addr, } *p = '\0'; - return string(buf, end, mac_addr, spec); + return string_nocheck(buf, end, mac_addr, spec); } static noinline_for_stack @@ -1313,7 +1320,7 @@ char *ip6_addr_string(char *buf, char *end, const u8 *addr, else ip6_string(ip6_addr, addr, fmt); - return string(buf, end, ip6_addr, spec); + return string_nocheck(buf, end, ip6_addr, spec); } static noinline_for_stack @@ -1324,7 +1331,7 @@ char *ip4_addr_string(char *buf, char *end, const u8 *addr, ip4_string(ip4_addr, addr, fmt); - return string(buf, end, ip4_addr, spec); + return string_nocheck(buf, end, ip4_addr, spec); } static noinline_for_stack @@ -1386,7 +1393,7 @@ char *ip6_addr_string_sa(char *buf, char *end, const struct sockaddr_in6 *sa, } *p = '\0'; - return string(buf, end, ip6_addr, spec); + return string_nocheck(buf, end, ip6_addr, spec); } static noinline_for_stack @@ -1421,7 +1428,7 @@ char *ip4_addr_string_sa(char *buf, char *end, const struct sockaddr_in *sa, } *p = '\0'; - return string(buf, end, ip4_addr, spec); + return string_nocheck(buf, end, ip4_addr, spec); } static noinline_for_stack @@ -1522,7 +1529,7 @@ char *uuid_string(char *buf, char *end, const u8 *addr, *p = 0; - return string(buf, end, uuid, spec); + return string_nocheck(buf, end, uuid, spec); } static noinline_for_stack @@ -1736,13 +1743,13 @@ char *device_node_gen_full_name(const struct device_node *np, char *buf, char *e /* special case for root node */ if (!parent) - return string(buf, end, "/", default_str_spec); + return string_nocheck(buf, end, "/", default_str_spec); for (depth = 0; parent->parent; depth++) parent = parent->parent; for ( ; depth >= 0; depth--) { - buf = string(buf, end, "/", default_str_spec); + buf = string_nocheck(buf, end, "/", default_str_spec); buf = string(buf, end, device_node_name_for_depth(np, depth), default_str_spec); } @@ -1770,10 +1777,10 @@ char *device_node_string(char *buf, char *end, struct device_node *dn, str_spec.field_width = -1; if (!IS_ENABLED(CONFIG_OF)) - return string(buf, end, "(!OF)", spec); + return string_nocheck(buf, end, "(!OF)", spec); if ((unsigned long)dn < PAGE_SIZE) - return string(buf, end, "(null)", spec); + return string_nocheck(buf, end, "(null)", spec); /* simple case without anything any more format specifiers */ fmt++; @@ -1814,7 +1821,7 @@ char *device_node_string(char *buf, char *end, struct device_node *dn, tbuf[2] = of_node_check_flag(dn, OF_POPULATED) ? 'P' : '-'; tbuf[3] = of_node_check_flag(dn, OF_POPULATED_BUS) ? 'B' : '-'; tbuf[4] = 0; - buf = string(buf, end, tbuf, str_spec); + buf = string_nocheck(buf, end, tbuf, str_spec); break; case 'c': /* major compatible string */ ret = of_property_read_string(dn, "compatible", &p); @@ -1825,10 +1832,10 @@ char *device_node_string(char *buf, char *end, struct device_node *dn, has_mult = false; of_property_for_each_string(dn, "compatible", prop, p) { if (has_mult) - buf = string(buf, end, ",", str_spec); - buf = string(buf, end, "\"", str_spec); + buf = string_nocheck(buf, end, ",", str_spec); + buf = string_nocheck(buf, end, "\"", str_spec); buf = string(buf, end, p, str_spec); - buf = string(buf, end, "\"", str_spec); + buf = string_nocheck(buf, end, "\"", str_spec); has_mult = true; } @@ -1966,7 +1973,7 @@ char *pointer(const char *fmt, char *buf, char *end, void *ptr, */ if (spec.field_width == -1) spec.field_width = default_width; - return string(buf, end, "(null)", spec); + return string_nocheck(buf, end, "(null)", spec); } switch (*fmt) { @@ -2022,7 +2029,7 @@ char *pointer(const char *fmt, char *buf, char *end, void *ptr, case AF_INET6: return ip6_addr_string_sa(buf, end, &sa->v6, spec, fmt); default: - return string(buf, end, "(invalid address)", spec); + return string_nocheck(buf, end, "(invalid address)", spec); }} } break; -- cgit From f00cc102b862be688fe090aec30e08d61a8f5e63 Mon Sep 17 00:00:00 2001 From: Petr Mladek Date: Wed, 17 Apr 2019 13:53:44 +0200 Subject: vsprintf: Factor out %p[iI] handler as ip_addr_string() Move the non-trivial code from the long pointer() function. We are going to improve error handling that will make it even more complicated. This patch does not change the existing behavior. Link: http://lkml.kernel.org/r/20190417115350.20479-5-pmladek@suse.com To: Rasmus Villemoes Cc: Linus Torvalds Cc: "Tobin C . Harding" Cc: Joe Perches Cc: Andrew Morton Cc: Michal Hocko Cc: Steven Rostedt Cc: Sergey Senozhatsky Cc: linux-kernel@vger.kernel.org Reviewed-by: Sergey Senozhatsky Reviewed-by: Andy Shevchenko Signed-off-by: Petr Mladek --- lib/vsprintf.c | 52 ++++++++++++++++++++++++++++++---------------------- 1 file changed, 30 insertions(+), 22 deletions(-) (limited to 'lib') diff --git a/lib/vsprintf.c b/lib/vsprintf.c index c9c9a1179870..8ca29bc0d786 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -1431,6 +1431,35 @@ char *ip4_addr_string_sa(char *buf, char *end, const struct sockaddr_in *sa, return string_nocheck(buf, end, ip4_addr, spec); } +static noinline_for_stack +char *ip_addr_string(char *buf, char *end, const void *ptr, + struct printf_spec spec, const char *fmt) +{ + switch (fmt[1]) { + case '6': + return ip6_addr_string(buf, end, ptr, spec, fmt); + case '4': + return ip4_addr_string(buf, end, ptr, spec, fmt); + case 'S': { + const union { + struct sockaddr raw; + struct sockaddr_in v4; + struct sockaddr_in6 v6; + } *sa = ptr; + + switch (sa->raw.sa_family) { + case AF_INET: + return ip4_addr_string_sa(buf, end, &sa->v4, spec, fmt); + case AF_INET6: + return ip6_addr_string_sa(buf, end, &sa->v6, spec, fmt); + default: + return string_nocheck(buf, end, "(invalid address)", spec); + }} + } + + return ptr_to_id(buf, end, ptr, spec); +} + static noinline_for_stack char *escaped_string(char *buf, char *end, u8 *addr, struct printf_spec spec, const char *fmt) @@ -2011,28 +2040,7 @@ char *pointer(const char *fmt, char *buf, char *end, void *ptr, * 4: 001.002.003.004 * 6: 000102...0f */ - switch (fmt[1]) { - case '6': - return ip6_addr_string(buf, end, ptr, spec, fmt); - case '4': - return ip4_addr_string(buf, end, ptr, spec, fmt); - case 'S': { - const union { - struct sockaddr raw; - struct sockaddr_in v4; - struct sockaddr_in6 v6; - } *sa = ptr; - - switch (sa->raw.sa_family) { - case AF_INET: - return ip4_addr_string_sa(buf, end, &sa->v4, spec, fmt); - case AF_INET6: - return ip6_addr_string_sa(buf, end, &sa->v6, spec, fmt); - default: - return string_nocheck(buf, end, "(invalid address)", spec); - }} - } - break; + return ip_addr_string(buf, end, ptr, spec, fmt); case 'E': return escaped_string(buf, end, ptr, spec, fmt); case 'U': -- cgit From 45c3e93d751ea50861c796da3cbfc848fa6ddf55 Mon Sep 17 00:00:00 2001 From: Petr Mladek Date: Wed, 17 Apr 2019 13:53:45 +0200 Subject: vsprintf: Factor out %pV handler as va_format() Move the code from the long pointer() function. We are going to improve error handling that will make it more complicated. This patch does not change the existing behavior. Link: http://lkml.kernel.org/r/20190417115350.20479-6-pmladek@suse.com To: Rasmus Villemoes Cc: Linus Torvalds Cc: "Tobin C . Harding" Cc: Joe Perches Cc: Andrew Morton Cc: Michal Hocko Cc: Steven Rostedt Cc: Sergey Senozhatsky Cc: linux-kernel@vger.kernel.org Reviewed-by: Sergey Senozhatsky Reviewed-by: Andy Shevchenko Signed-off-by: Petr Mladek --- lib/vsprintf.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) (limited to 'lib') diff --git a/lib/vsprintf.c b/lib/vsprintf.c index 8ca29bc0d786..12b71a4d4613 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -1520,6 +1520,17 @@ char *escaped_string(char *buf, char *end, u8 *addr, struct printf_spec spec, return buf; } +static char *va_format(char *buf, char *end, struct va_format *va_fmt) +{ + va_list va; + + va_copy(va, *va_fmt->va); + buf += vsnprintf(buf, end > buf ? end - buf : 0, va_fmt->fmt, va); + va_end(va); + + return buf; +} + static noinline_for_stack char *uuid_string(char *buf, char *end, const u8 *addr, struct printf_spec spec, const char *fmt) @@ -2046,15 +2057,7 @@ char *pointer(const char *fmt, char *buf, char *end, void *ptr, case 'U': return uuid_string(buf, end, ptr, spec, fmt); case 'V': - { - va_list va; - - va_copy(va, *((struct va_format *)ptr)->va); - buf += vsnprintf(buf, end > buf ? end - buf : 0, - ((struct va_format *)ptr)->fmt, va); - va_end(va); - return buf; - } + return va_format(buf, end, ptr); case 'K': return restricted_pointer(buf, end, ptr, spec); case 'N': -- cgit From 798cc27a305e7b35b7bff3a71257e6fe57f70bc1 Mon Sep 17 00:00:00 2001 From: Petr Mladek Date: Wed, 17 Apr 2019 13:53:46 +0200 Subject: vsprintf: Factor out %pO handler as kobject_string() Move code from the long pointer() function. We are going to improve error handling that will make it even more complicated. This patch does not change the existing behavior. Link: http://lkml.kernel.org/r/20190417115350.20479-7-pmladek@suse.com To: Rasmus Villemoes Cc: Linus Torvalds Cc: "Tobin C . Harding" Cc: Joe Perches Cc: Andrew Morton Cc: Michal Hocko Cc: Steven Rostedt Cc: Sergey Senozhatsky Cc: linux-kernel@vger.kernel.org Cc: Kees Cook Reviewed-by: Sergey Senozhatsky Reviewed-by: Andy Shevchenko Signed-off-by: Petr Mladek --- lib/vsprintf.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) (limited to 'lib') diff --git a/lib/vsprintf.c b/lib/vsprintf.c index 12b71a4d4613..9817d171f608 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -1888,6 +1888,17 @@ char *device_node_string(char *buf, char *end, struct device_node *dn, return widen_string(buf, buf - buf_start, end, spec); } +static char *kobject_string(char *buf, char *end, void *ptr, + struct printf_spec spec, const char *fmt) +{ + switch (fmt[1]) { + case 'F': + return device_node_string(buf, end, ptr, spec, fmt + 1); + } + + return ptr_to_id(buf, end, ptr, spec); +} + /* * Show a '%p' thing. A kernel extension is that the '%p' is followed * by an extra set of alphanumeric characters that are extended format @@ -2082,11 +2093,7 @@ char *pointer(const char *fmt, char *buf, char *end, void *ptr, case 'G': return flags_string(buf, end, ptr, fmt); case 'O': - switch (fmt[1]) { - case 'F': - return device_node_string(buf, end, ptr, spec, fmt + 1); - } - break; + return kobject_string(buf, end, ptr, spec, fmt); case 'x': return pointer_string(buf, end, ptr, spec); } -- cgit From 0b74d4d763fd4ee9daa53889324300587c015338 Mon Sep 17 00:00:00 2001 From: Petr Mladek Date: Wed, 17 Apr 2019 13:53:47 +0200 Subject: vsprintf: Consolidate handling of unknown pointer specifiers There are few printk formats that make sense only with two or more specifiers. Also some specifiers make sense only when a kernel feature is enabled. The handling of unknown specifiers is inconsistent and not helpful. Using WARN() looks like an overkill for this type of error. pr_warn() is not good either. It would by handled via printk_safe buffer and it might be hard to match it with the problematic string. A reasonable compromise seems to be writing the unknown format specifier into the original string with a question mark, for example (%pC?). It should be self-explaining enough. Note that it is in brackets to follow the (null) style. Note that it introduces a warning about that test_hashed() function is unused. It is going to be used again by a later patch. Link: http://lkml.kernel.org/r/20190417115350.20479-8-pmladek@suse.com To: Rasmus Villemoes Cc: Linus Torvalds Cc: "Tobin C . Harding" Cc: Joe Perches Cc: Andrew Morton Cc: Michal Hocko Cc: Steven Rostedt Cc: Sergey Senozhatsky Cc: linux-kernel@vger.kernel.org Reviewed-by: Sergey Senozhatsky Reviewed-by: Andy Shevchenko Signed-off-by: Petr Mladek --- lib/test_printf.c | 3 +-- lib/vsprintf.c | 28 +++++++++++++++++----------- 2 files changed, 18 insertions(+), 13 deletions(-) (limited to 'lib') diff --git a/lib/test_printf.c b/lib/test_printf.c index 659b6cc0d483..250ee864b8b8 100644 --- a/lib/test_printf.c +++ b/lib/test_printf.c @@ -462,8 +462,7 @@ struct_rtc_time(void) .tm_year = 118, }; - test_hashed("%pt", &tm); - + test("(%ptR?)", "%pt", &tm); test("2018-11-26T05:35:43", "%ptR", &tm); test("0118-10-26T05:35:43", "%ptRr", &tm); test("05:35:43|2018-11-26", "%ptRt|%ptRd", &tm, &tm); diff --git a/lib/vsprintf.c b/lib/vsprintf.c index 9817d171f608..f471a658422f 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -1435,6 +1435,8 @@ static noinline_for_stack char *ip_addr_string(char *buf, char *end, const void *ptr, struct printf_spec spec, const char *fmt) { + char *err_fmt_msg; + switch (fmt[1]) { case '6': return ip6_addr_string(buf, end, ptr, spec, fmt); @@ -1457,7 +1459,8 @@ char *ip_addr_string(char *buf, char *end, const void *ptr, }} } - return ptr_to_id(buf, end, ptr, spec); + err_fmt_msg = fmt[0] == 'i' ? "(%pi?)" : "(%pI?)"; + return string_nocheck(buf, end, err_fmt_msg, spec); } static noinline_for_stack @@ -1585,7 +1588,7 @@ char *netdev_bits(char *buf, char *end, const void *addr, size = sizeof(netdev_features_t); break; default: - return ptr_to_id(buf, end, addr, spec); + return string_nocheck(buf, end, "(%pN?)", spec); } return special_hex_number(buf, end, num, size); @@ -1689,7 +1692,7 @@ char *time_and_date(char *buf, char *end, void *ptr, struct printf_spec spec, case 'R': return rtc_str(buf, end, (const struct rtc_time *)ptr, fmt); default: - return ptr_to_id(buf, end, ptr, spec); + return string_nocheck(buf, end, "(%ptR?)", spec); } } @@ -1697,7 +1700,10 @@ static noinline_for_stack char *clock(char *buf, char *end, struct clk *clk, struct printf_spec spec, const char *fmt) { - if (!IS_ENABLED(CONFIG_HAVE_CLK) || !clk) + if (!IS_ENABLED(CONFIG_HAVE_CLK)) + return string_nocheck(buf, end, "(%pC?)", spec); + + if (!clk) return string(buf, end, NULL, spec); switch (fmt[1]) { @@ -1706,7 +1712,7 @@ char *clock(char *buf, char *end, struct clk *clk, struct printf_spec spec, #ifdef CONFIG_COMMON_CLK return string(buf, end, __clk_get_name(clk), spec); #else - return ptr_to_id(buf, end, clk, spec); + return string_nocheck(buf, end, "(%pC?)", spec); #endif } } @@ -1739,7 +1745,8 @@ char *format_flags(char *buf, char *end, unsigned long flags, } static noinline_for_stack -char *flags_string(char *buf, char *end, void *flags_ptr, const char *fmt) +char *flags_string(char *buf, char *end, void *flags_ptr, + struct printf_spec spec, const char *fmt) { unsigned long flags; const struct trace_print_flags *names; @@ -1760,8 +1767,7 @@ char *flags_string(char *buf, char *end, void *flags_ptr, const char *fmt) names = gfpflag_names; break; default: - WARN_ONCE(1, "Unsupported flags modifier: %c\n", fmt[1]); - return buf; + return string_nocheck(buf, end, "(%pG?)", spec); } return format_flags(buf, end, flags, names); @@ -1817,7 +1823,7 @@ char *device_node_string(char *buf, char *end, struct device_node *dn, str_spec.field_width = -1; if (!IS_ENABLED(CONFIG_OF)) - return string_nocheck(buf, end, "(!OF)", spec); + return string_nocheck(buf, end, "(%pOF?)", spec); if ((unsigned long)dn < PAGE_SIZE) return string_nocheck(buf, end, "(null)", spec); @@ -1896,7 +1902,7 @@ static char *kobject_string(char *buf, char *end, void *ptr, return device_node_string(buf, end, ptr, spec, fmt + 1); } - return ptr_to_id(buf, end, ptr, spec); + return string_nocheck(buf, end, "(%pO?)", spec); } /* @@ -2091,7 +2097,7 @@ char *pointer(const char *fmt, char *buf, char *end, void *ptr, #endif case 'G': - return flags_string(buf, end, ptr, fmt); + return flags_string(buf, end, ptr, spec, fmt); case 'O': return kobject_string(buf, end, ptr, spec, fmt); case 'x': -- cgit From 3e5903eb9cff707301712498aed9e34b3e2ee883 Mon Sep 17 00:00:00 2001 From: Petr Mladek Date: Wed, 17 Apr 2019 13:53:48 +0200 Subject: vsprintf: Prevent crash when dereferencing invalid pointers We already prevent crash when dereferencing some obviously broken pointers. But the handling is not consistent. Sometimes we print "(null)" only for pure NULL pointer, sometimes for pointers in the first page and sometimes also for pointers in the last page (error codes). Note that printk() call this code under logbuf_lock. Any recursive printks are redirected to the printk_safe implementation and the messages are stored into per-CPU buffers. These buffers might be eventually flushed in printk_safe_flush_on_panic() but it is not guaranteed. This patch adds a check using probe_kernel_read(). It is not a full-proof test. But it should help to see the error message in 99% situations where the kernel would silently crash otherwise. Also it makes the error handling unified for "%s" and the many %p* specifiers that need to read the data from a given address. We print: + (null) when accessing data on pure pure NULL address + (efault) when accessing data on an invalid address It does not affect the %p* specifiers that just print the given address in some form, namely %pF, %pf, %pS, %ps, %pB, %pK, %px, and plain %p. Note that we print (efault) from security reasons. In fact, the real address can be seen only by %px or eventually %pK. Link: http://lkml.kernel.org/r/20190417115350.20479-9-pmladek@suse.com To: Rasmus Villemoes Cc: Linus Torvalds Cc: "Tobin C . Harding" Cc: Joe Perches Cc: Andrew Morton Cc: Michal Hocko Cc: Steven Rostedt Cc: Sergey Senozhatsky Cc: linux-kernel@vger.kernel.org Reviewed-by: Sergey Senozhatsky Reviewed-by: Andy Shevchenko Signed-off-by: Petr Mladek --- lib/test_printf.c | 22 ++++++++- lib/vsprintf.c | 136 ++++++++++++++++++++++++++++++++++++++++-------------- 2 files changed, 122 insertions(+), 36 deletions(-) (limited to 'lib') diff --git a/lib/test_printf.c b/lib/test_printf.c index 250ee864b8b8..359ae4fb1ece 100644 --- a/lib/test_printf.c +++ b/lib/test_printf.c @@ -239,6 +239,7 @@ plain_format(void) #define PTR ((void *)0x456789ab) #define PTR_STR "456789ab" #define PTR_VAL_NO_CRNG "(ptrval)" +#define ZEROS "" static int __init plain_format(void) @@ -268,7 +269,6 @@ plain_hash_to_buffer(const void *p, char *buf, size_t len) return 0; } - static int __init plain_hash(void) { @@ -325,6 +325,24 @@ test_hashed(const char *fmt, const void *p) test(buf, fmt, p); } +static void __init +null_pointer(void) +{ + test_hashed("%p", NULL); + test(ZEROS "00000000", "%px", NULL); + test("(null)", "%pE", NULL); +} + +#define PTR_INVALID ((void *)0x000000ab) + +static void __init +invalid_pointer(void) +{ + test_hashed("%p", PTR_INVALID); + test(ZEROS "000000ab", "%px", PTR_INVALID); + test("(efault)", "%pE", PTR_INVALID); +} + static void __init symbol_ptr(void) { @@ -571,6 +589,8 @@ static void __init test_pointer(void) { plain(); + null_pointer(); + invalid_pointer(); symbol_ptr(); kernel_ptr(); struct_resource(); diff --git a/lib/vsprintf.c b/lib/vsprintf.c index f471a658422f..b989f1e8f35b 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -612,12 +612,45 @@ static char *string_nocheck(char *buf, char *end, const char *s, return widen_string(buf, len, end, spec); } +/* + * This is not a fool-proof test. 99% of the time that this will fault is + * due to a bad pointer, not one that crosses into bad memory. Just test + * the address to make sure it doesn't fault due to a poorly added printk + * during debugging. + */ +static const char *check_pointer_msg(const void *ptr) +{ + char byte; + + if (!ptr) + return "(null)"; + + if (probe_kernel_address(ptr, byte)) + return "(efault)"; + + return NULL; +} + +static int check_pointer(char **buf, char *end, const void *ptr, + struct printf_spec spec) +{ + const char *err_msg; + + err_msg = check_pointer_msg(ptr); + if (err_msg) { + *buf = string_nocheck(*buf, end, err_msg, spec); + return -EFAULT; + } + + return 0; +} + static noinline_for_stack char *string(char *buf, char *end, const char *s, struct printf_spec spec) { - if ((unsigned long)s < PAGE_SIZE) - s = "(null)"; + if (check_pointer(&buf, end, s, spec)) + return buf; return string_nocheck(buf, end, s, spec); } @@ -792,6 +825,11 @@ char *dentry_name(char *buf, char *end, const struct dentry *d, struct printf_sp rcu_read_lock(); for (i = 0; i < depth; i++, d = p) { + if (check_pointer(&buf, end, d, spec)) { + rcu_read_unlock(); + return buf; + } + p = READ_ONCE(d->d_parent); array[i] = READ_ONCE(d->d_name.name); if (p == d) { @@ -822,8 +860,12 @@ static noinline_for_stack char *bdev_name(char *buf, char *end, struct block_device *bdev, struct printf_spec spec, const char *fmt) { - struct gendisk *hd = bdev->bd_disk; - + struct gendisk *hd; + + if (check_pointer(&buf, end, bdev, spec)) + return buf; + + hd = bdev->bd_disk; buf = string(buf, end, hd->disk_name, spec); if (bdev->bd_part->partno) { if (isdigit(hd->disk_name[strlen(hd->disk_name)-1])) { @@ -942,6 +984,9 @@ char *resource_string(char *buf, char *end, struct resource *res, int decode = (fmt[0] == 'R') ? 1 : 0; const struct printf_spec *specp; + if (check_pointer(&buf, end, res, spec)) + return buf; + *p++ = '['; if (res->flags & IORESOURCE_IO) { p = string_nocheck(p, pend, "io ", str_spec); @@ -1004,9 +1049,8 @@ char *hex_string(char *buf, char *end, u8 *addr, struct printf_spec spec, /* nothing to print */ return buf; - if (ZERO_OR_NULL_PTR(addr)) - /* NULL pointer */ - return string(buf, end, NULL, spec); + if (check_pointer(&buf, end, addr, spec)) + return buf; switch (fmt[1]) { case 'C': @@ -1053,6 +1097,9 @@ char *bitmap_string(char *buf, char *end, unsigned long *bitmap, int i, chunksz; bool first = true; + if (check_pointer(&buf, end, bitmap, spec)) + return buf; + /* reused to print numbers */ spec = (struct printf_spec){ .flags = SMALL | ZEROPAD, .base = 16 }; @@ -1094,6 +1141,9 @@ char *bitmap_list_string(char *buf, char *end, unsigned long *bitmap, int cur, rbot, rtop; bool first = true; + if (check_pointer(&buf, end, bitmap, spec)) + return buf; + rbot = cur = find_first_bit(bitmap, nr_bits); while (cur < nr_bits) { rtop = cur; @@ -1132,6 +1182,9 @@ char *mac_address_string(char *buf, char *end, u8 *addr, char separator; bool reversed = false; + if (check_pointer(&buf, end, addr, spec)) + return buf; + switch (fmt[1]) { case 'F': separator = '-'; @@ -1437,6 +1490,9 @@ char *ip_addr_string(char *buf, char *end, const void *ptr, { char *err_fmt_msg; + if (check_pointer(&buf, end, ptr, spec)) + return buf; + switch (fmt[1]) { case '6': return ip6_addr_string(buf, end, ptr, spec, fmt); @@ -1475,9 +1531,8 @@ char *escaped_string(char *buf, char *end, u8 *addr, struct printf_spec spec, if (spec.field_width == 0) return buf; /* nothing to print */ - if (ZERO_OR_NULL_PTR(addr)) - return string(buf, end, NULL, spec); /* NULL pointer */ - + if (check_pointer(&buf, end, addr, spec)) + return buf; do { switch (fmt[count++]) { @@ -1523,10 +1578,14 @@ char *escaped_string(char *buf, char *end, u8 *addr, struct printf_spec spec, return buf; } -static char *va_format(char *buf, char *end, struct va_format *va_fmt) +static char *va_format(char *buf, char *end, struct va_format *va_fmt, + struct printf_spec spec, const char *fmt) { va_list va; + if (check_pointer(&buf, end, va_fmt, spec)) + return buf; + va_copy(va, *va_fmt->va); buf += vsnprintf(buf, end > buf ? end - buf : 0, va_fmt->fmt, va); va_end(va); @@ -1544,6 +1603,9 @@ char *uuid_string(char *buf, char *end, const u8 *addr, const u8 *index = uuid_index; bool uc = false; + if (check_pointer(&buf, end, addr, spec)) + return buf; + switch (*(++fmt)) { case 'L': uc = true; /* fall-through */ @@ -1582,6 +1644,9 @@ char *netdev_bits(char *buf, char *end, const void *addr, unsigned long long num; int size; + if (check_pointer(&buf, end, addr, spec)) + return buf; + switch (fmt[1]) { case 'F': num = *(const netdev_features_t *)addr; @@ -1595,11 +1660,15 @@ char *netdev_bits(char *buf, char *end, const void *addr, } static noinline_for_stack -char *address_val(char *buf, char *end, const void *addr, const char *fmt) +char *address_val(char *buf, char *end, const void *addr, + struct printf_spec spec, const char *fmt) { unsigned long long num; int size; + if (check_pointer(&buf, end, addr, spec)) + return buf; + switch (fmt[1]) { case 'd': num = *(const dma_addr_t *)addr; @@ -1651,12 +1720,16 @@ char *time_str(char *buf, char *end, const struct rtc_time *tm, bool r) } static noinline_for_stack -char *rtc_str(char *buf, char *end, const struct rtc_time *tm, const char *fmt) +char *rtc_str(char *buf, char *end, const struct rtc_time *tm, + struct printf_spec spec, const char *fmt) { bool have_t = true, have_d = true; bool raw = false; int count = 2; + if (check_pointer(&buf, end, tm, spec)) + return buf; + switch (fmt[count]) { case 'd': have_t = false; @@ -1690,7 +1763,7 @@ char *time_and_date(char *buf, char *end, void *ptr, struct printf_spec spec, { switch (fmt[1]) { case 'R': - return rtc_str(buf, end, (const struct rtc_time *)ptr, fmt); + return rtc_str(buf, end, (const struct rtc_time *)ptr, spec, fmt); default: return string_nocheck(buf, end, "(%ptR?)", spec); } @@ -1703,8 +1776,8 @@ char *clock(char *buf, char *end, struct clk *clk, struct printf_spec spec, if (!IS_ENABLED(CONFIG_HAVE_CLK)) return string_nocheck(buf, end, "(%pC?)", spec); - if (!clk) - return string(buf, end, NULL, spec); + if (check_pointer(&buf, end, clk, spec)) + return buf; switch (fmt[1]) { case 'n': @@ -1751,6 +1824,9 @@ char *flags_string(char *buf, char *end, void *flags_ptr, unsigned long flags; const struct trace_print_flags *names; + if (check_pointer(&buf, end, flags_ptr, spec)) + return buf; + switch (fmt[1]) { case 'p': flags = *(unsigned long *)flags_ptr; @@ -1825,8 +1901,8 @@ char *device_node_string(char *buf, char *end, struct device_node *dn, if (!IS_ENABLED(CONFIG_OF)) return string_nocheck(buf, end, "(%pOF?)", spec); - if ((unsigned long)dn < PAGE_SIZE) - return string_nocheck(buf, end, "(null)", spec); + if (check_pointer(&buf, end, dn, spec)) + return buf; /* simple case without anything any more format specifiers */ fmt++; @@ -2021,18 +2097,6 @@ static noinline_for_stack char *pointer(const char *fmt, char *buf, char *end, void *ptr, struct printf_spec spec) { - const int default_width = 2 * sizeof(void *); - - if (!ptr && *fmt != 'K' && *fmt != 'x') { - /* - * Print (null) with the same width as a pointer so it makes - * tabular output look nice. - */ - if (spec.field_width == -1) - spec.field_width = default_width; - return string_nocheck(buf, end, "(null)", spec); - } - switch (*fmt) { case 'F': case 'f': @@ -2074,13 +2138,13 @@ char *pointer(const char *fmt, char *buf, char *end, void *ptr, case 'U': return uuid_string(buf, end, ptr, spec, fmt); case 'V': - return va_format(buf, end, ptr); + return va_format(buf, end, ptr, spec, fmt); case 'K': return restricted_pointer(buf, end, ptr, spec); case 'N': return netdev_bits(buf, end, ptr, spec, fmt); case 'a': - return address_val(buf, end, ptr, fmt); + return address_val(buf, end, ptr, spec, fmt); case 'd': return dentry_name(buf, end, ptr, spec, fmt); case 't': @@ -2714,11 +2778,13 @@ int vbin_printf(u32 *bin_buf, size_t size, const char *fmt, va_list args) case FORMAT_TYPE_STR: { const char *save_str = va_arg(args, char *); + const char *err_msg; size_t len; - if ((unsigned long)save_str > (unsigned long)-PAGE_SIZE - || (unsigned long)save_str < PAGE_SIZE) - save_str = "(null)"; + err_msg = check_pointer_msg(save_str); + if (err_msg) + save_str = err_msg; + len = strlen(save_str) + 1; if (str + len < end) memcpy(str, save_str, len); -- cgit From 635720ac75a51092b456bed517ff170047883252 Mon Sep 17 00:00:00 2001 From: Petr Mladek Date: Wed, 17 Apr 2019 13:53:49 +0200 Subject: vsprintf: Avoid confusion between invalid address and value We are able to detect invalid values handled by %p[iI] printk specifier. The current error message is "invalid address". It might cause confusion against "(efault)" reported by the generic valid_pointer_address() check. Let's unify the style and use the more appropriate error code description "(einval)". Link: http://lkml.kernel.org/r/20190417115350.20479-10-pmladek@suse.com To: Rasmus Villemoes Cc: Linus Torvalds Cc: "Tobin C . Harding" Cc: Joe Perches Cc: Andrew Morton Cc: Michal Hocko Cc: Steven Rostedt Cc: Sergey Senozhatsky Cc: linux-kernel@vger.kernel.org Reviewed-by: Sergey Senozhatsky Reviewed-by: Andy Shevchenko Signed-off-by: Petr Mladek --- lib/vsprintf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/vsprintf.c b/lib/vsprintf.c index b989f1e8f35b..4e5666035b74 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -1511,7 +1511,7 @@ char *ip_addr_string(char *buf, char *end, const void *ptr, case AF_INET6: return ip6_addr_string_sa(buf, end, &sa->v6, spec, fmt); default: - return string_nocheck(buf, end, "(invalid address)", spec); + return string_nocheck(buf, end, "(einval)", spec); }} } -- cgit From c8c3b584343cb7522fc00322769a9f288305743f Mon Sep 17 00:00:00 2001 From: Petr Mladek Date: Wed, 17 Apr 2019 13:53:50 +0200 Subject: vsprintf: Limit the length of inlined error messages The inlined error messages must be used carefully because they need to fit into the given buffer. Handle them using a custom wrapper that makes people aware of the problem. Also define a reasonable hard limit to avoid a completely insane usage. Suggested-by: Sergey Senozhatsky Link: http://lkml.kernel.org/r/20190417115350.20479-11-pmladek@suse.com To: Rasmus Villemoes Cc: Linus Torvalds Cc: "Tobin C . Harding" Cc: Joe Perches Cc: Andrew Morton Cc: Michal Hocko Cc: Steven Rostedt Cc: Sergey Senozhatsky Cc: linux-kernel@vger.kernel.org Reviewed-by: Sergey Senozhatsky Reviewed-by: Andy Shevchenko Signed-off-by: Petr Mladek --- lib/vsprintf.c | 39 +++++++++++++++++++++++++++------------ 1 file changed, 27 insertions(+), 12 deletions(-) (limited to 'lib') diff --git a/lib/vsprintf.c b/lib/vsprintf.c index 4e5666035b74..1f367f3a7e2b 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -612,6 +612,21 @@ static char *string_nocheck(char *buf, char *end, const char *s, return widen_string(buf, len, end, spec); } +/* Be careful: error messages must fit into the given buffer. */ +static char *error_string(char *buf, char *end, const char *s, + struct printf_spec spec) +{ + /* + * Hard limit to avoid a completely insane messages. It actually + * works pretty well because most error messages are in + * the many pointer format modifiers. + */ + if (spec.precision == -1) + spec.precision = 2 * sizeof(void *); + + return string_nocheck(buf, end, s, spec); +} + /* * This is not a fool-proof test. 99% of the time that this will fault is * due to a bad pointer, not one that crosses into bad memory. Just test @@ -638,7 +653,7 @@ static int check_pointer(char **buf, char *end, const void *ptr, err_msg = check_pointer_msg(ptr); if (err_msg) { - *buf = string_nocheck(*buf, end, err_msg, spec); + *buf = error_string(*buf, end, err_msg, spec); return -EFAULT; } @@ -741,7 +756,7 @@ static char *ptr_to_id(char *buf, char *end, const void *ptr, if (static_branch_unlikely(¬_filled_random_ptr_key)) { spec.field_width = 2 * sizeof(ptr); /* string length must be less than default_width */ - return string_nocheck(buf, end, str, spec); + return error_string(buf, end, str, spec); } #ifdef CONFIG_64BIT @@ -777,7 +792,7 @@ char *restricted_pointer(char *buf, char *end, const void *ptr, if (in_irq() || in_serving_softirq() || in_nmi()) { if (spec.field_width == -1) spec.field_width = 2 * sizeof(ptr); - return string_nocheck(buf, end, "pK-error", spec); + return error_string(buf, end, "pK-error", spec); } /* @@ -1511,12 +1526,12 @@ char *ip_addr_string(char *buf, char *end, const void *ptr, case AF_INET6: return ip6_addr_string_sa(buf, end, &sa->v6, spec, fmt); default: - return string_nocheck(buf, end, "(einval)", spec); + return error_string(buf, end, "(einval)", spec); }} } err_fmt_msg = fmt[0] == 'i' ? "(%pi?)" : "(%pI?)"; - return string_nocheck(buf, end, err_fmt_msg, spec); + return error_string(buf, end, err_fmt_msg, spec); } static noinline_for_stack @@ -1653,7 +1668,7 @@ char *netdev_bits(char *buf, char *end, const void *addr, size = sizeof(netdev_features_t); break; default: - return string_nocheck(buf, end, "(%pN?)", spec); + return error_string(buf, end, "(%pN?)", spec); } return special_hex_number(buf, end, num, size); @@ -1765,7 +1780,7 @@ char *time_and_date(char *buf, char *end, void *ptr, struct printf_spec spec, case 'R': return rtc_str(buf, end, (const struct rtc_time *)ptr, spec, fmt); default: - return string_nocheck(buf, end, "(%ptR?)", spec); + return error_string(buf, end, "(%ptR?)", spec); } } @@ -1774,7 +1789,7 @@ char *clock(char *buf, char *end, struct clk *clk, struct printf_spec spec, const char *fmt) { if (!IS_ENABLED(CONFIG_HAVE_CLK)) - return string_nocheck(buf, end, "(%pC?)", spec); + return error_string(buf, end, "(%pC?)", spec); if (check_pointer(&buf, end, clk, spec)) return buf; @@ -1785,7 +1800,7 @@ char *clock(char *buf, char *end, struct clk *clk, struct printf_spec spec, #ifdef CONFIG_COMMON_CLK return string(buf, end, __clk_get_name(clk), spec); #else - return string_nocheck(buf, end, "(%pC?)", spec); + return error_string(buf, end, "(%pC?)", spec); #endif } } @@ -1843,7 +1858,7 @@ char *flags_string(char *buf, char *end, void *flags_ptr, names = gfpflag_names; break; default: - return string_nocheck(buf, end, "(%pG?)", spec); + return error_string(buf, end, "(%pG?)", spec); } return format_flags(buf, end, flags, names); @@ -1899,7 +1914,7 @@ char *device_node_string(char *buf, char *end, struct device_node *dn, str_spec.field_width = -1; if (!IS_ENABLED(CONFIG_OF)) - return string_nocheck(buf, end, "(%pOF?)", spec); + return error_string(buf, end, "(%pOF?)", spec); if (check_pointer(&buf, end, dn, spec)) return buf; @@ -1978,7 +1993,7 @@ static char *kobject_string(char *buf, char *end, void *ptr, return device_node_string(buf, end, ptr, spec, fmt + 1); } - return string_nocheck(buf, end, "(%pO?)", spec); + return error_string(buf, end, "(%pO?)", spec); } /* -- cgit From ce9d3eceb7ffb74445a8d892ca0685395a93a7e2 Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Sat, 27 Apr 2019 00:46:30 +0800 Subject: lib/vsprintf: Make function pointer_string static Fix sparse warning: lib/vsprintf.c:673:6: warning: symbol 'pointer_string' was not declared. Should it be static? Link: http://lkml.kernel.org/r/20190426164630.22104-1-yuehaibing@huawei.com To: To: To: To: Signed-off-by: YueHaibing Signed-off-by: Petr Mladek --- lib/vsprintf.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/vsprintf.c b/lib/vsprintf.c index 1f367f3a7e2b..7b0a6140bfad 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -670,8 +670,9 @@ char *string(char *buf, char *end, const char *s, return string_nocheck(buf, end, s, spec); } -char *pointer_string(char *buf, char *end, const void *ptr, - struct printf_spec spec) +static char *pointer_string(char *buf, char *end, + const void *ptr, + struct printf_spec spec) { spec.base = 16; spec.flags |= SMALL; -- cgit