Loading Documentation/core-api/printk-formats.rst +8 −0 Original line number Diff line number Diff line Loading @@ -58,6 +58,14 @@ A raw pointer value may be printed with %p which will hash the address before printing. The kernel also supports extended specifiers for printing pointers of different types. Some of the extended specifiers print the data on the given address instead of printing the address itself. In this case, the following error messages might be printed instead of the unreachable information:: (null) data on plain NULL address (efault) data on invalid address (einval) invalid data on a valid address Plain Pointers -------------- Loading lib/test_printf.c +22 −3 Original line number Diff line number Diff line Loading @@ -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) Loading Loading @@ -268,7 +269,6 @@ plain_hash_to_buffer(const void *p, char *buf, size_t len) return 0; } static int __init plain_hash(void) { Loading Loading @@ -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) { Loading Loading @@ -462,8 +480,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); Loading Loading @@ -572,6 +589,8 @@ static void __init test_pointer(void) { plain(); null_pointer(); invalid_pointer(); symbol_ptr(); kernel_ptr(); struct_resource(); Loading lib/vsprintf.c +271 −160 Original line number Diff line number Diff line Loading @@ -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) Loading @@ -614,8 +612,66 @@ char *string(char *buf, char *end, const char *s, struct printf_spec spec) 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 * 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 = error_string(*buf, end, err_msg, spec); return -EFAULT; } return 0; } static noinline_for_stack char *pointer_string(char *buf, char *end, const void *ptr, char *string(char *buf, char *end, const char *s, struct printf_spec spec) { if (check_pointer(&buf, end, s, spec)) return buf; return string_nocheck(buf, end, s, spec); } static char *pointer_string(char *buf, char *end, const void *ptr, struct printf_spec spec) { spec.base = 16; Loading Loading @@ -701,7 +757,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 error_string(buf, end, str, spec); } #ifdef CONFIG_64BIT Loading @@ -717,6 +773,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: /* Handle as %p, hash and do _not_ leak addresses. */ return ptr_to_id(buf, end, ptr, spec); 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 error_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) Loading @@ -736,6 +841,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) { Loading Loading @@ -766,8 +876,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])) { Loading Loading @@ -802,7 +916,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 Loading Loading @@ -886,29 +1000,32 @@ 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(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); Loading @@ -919,21 +1036,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 Loading @@ -948,9 +1065,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': Loading Loading @@ -997,6 +1113,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 }; Loading Loading @@ -1038,6 +1157,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; Loading Loading @@ -1076,6 +1198,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 = '-'; Loading @@ -1101,7 +1226,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 Loading Loading @@ -1264,7 +1389,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 Loading @@ -1275,7 +1400,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 Loading Loading @@ -1337,7 +1462,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 Loading Loading @@ -1372,7 +1497,42 @@ 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 char *ip_addr_string(char *buf, char *end, const void *ptr, struct printf_spec spec, const char *fmt) { 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); 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 error_string(buf, end, "(einval)", spec); }} } err_fmt_msg = fmt[0] == 'i' ? "(%pi?)" : "(%pI?)"; return error_string(buf, end, err_fmt_msg, spec); } static noinline_for_stack Loading @@ -1387,9 +1547,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++]) { Loading Loading @@ -1435,6 +1594,21 @@ 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, 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); return buf; } static noinline_for_stack char *uuid_string(char *buf, char *end, const u8 *addr, struct printf_spec spec, const char *fmt) Loading @@ -1445,6 +1619,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 */ Loading Loading @@ -1473,56 +1650,7 @@ char *uuid_string(char *buf, char *end, const u8 *addr, *p = 0; 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); return string_nocheck(buf, end, uuid, spec); } static noinline_for_stack Loading @@ -1532,24 +1660,31 @@ 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; size = sizeof(netdev_features_t); break; default: return ptr_to_id(buf, end, addr, spec); return error_string(buf, end, "(%pN?)", spec); } return special_hex_number(buf, end, num, size); } 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; Loading Loading @@ -1601,12 +1736,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; Loading Loading @@ -1640,9 +1779,9 @@ 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 ptr_to_id(buf, end, ptr, spec); return error_string(buf, end, "(%ptR?)", spec); } } Loading @@ -1650,8 +1789,11 @@ 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) return string(buf, end, NULL, spec); if (!IS_ENABLED(CONFIG_HAVE_CLK)) return error_string(buf, end, "(%pC?)", spec); if (check_pointer(&buf, end, clk, spec)) return buf; switch (fmt[1]) { case 'n': Loading @@ -1659,7 +1801,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 error_string(buf, end, "(%pC?)", spec); #endif } } Loading Loading @@ -1692,11 +1834,15 @@ 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; if (check_pointer(&buf, end, flags_ptr, spec)) return buf; switch (fmt[1]) { case 'p': flags = *(unsigned long *)flags_ptr; Loading @@ -1713,8 +1859,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 error_string(buf, end, "(%pG?)", spec); } return format_flags(buf, end, flags, names); Loading @@ -1736,13 +1881,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); } Loading Loading @@ -1770,10 +1915,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 error_string(buf, end, "(%pOF?)", spec); if ((unsigned long)dn < PAGE_SIZE) return string(buf, end, "(null)", spec); if (check_pointer(&buf, end, dn, spec)) return buf; /* simple case without anything any more format specifiers */ fmt++; Loading Loading @@ -1814,7 +1959,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); Loading @@ -1825,10 +1970,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; } Loading @@ -1841,6 +1986,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 error_string(buf, end, "(%pO?)", 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 Loading Loading @@ -1957,18 +2113,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(buf, end, "(null)", spec); } switch (*fmt) { case 'F': case 'f': Loading Loading @@ -2004,50 +2148,19 @@ 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(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': 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, spec, fmt); case 'K': if (!kptr_restrict) break; 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': Loading @@ -2064,13 +2177,9 @@ 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': 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); } Loading Loading @@ -2685,11 +2794,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); Loading Loading
Documentation/core-api/printk-formats.rst +8 −0 Original line number Diff line number Diff line Loading @@ -58,6 +58,14 @@ A raw pointer value may be printed with %p which will hash the address before printing. The kernel also supports extended specifiers for printing pointers of different types. Some of the extended specifiers print the data on the given address instead of printing the address itself. In this case, the following error messages might be printed instead of the unreachable information:: (null) data on plain NULL address (efault) data on invalid address (einval) invalid data on a valid address Plain Pointers -------------- Loading
lib/test_printf.c +22 −3 Original line number Diff line number Diff line Loading @@ -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) Loading Loading @@ -268,7 +269,6 @@ plain_hash_to_buffer(const void *p, char *buf, size_t len) return 0; } static int __init plain_hash(void) { Loading Loading @@ -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) { Loading Loading @@ -462,8 +480,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); Loading Loading @@ -572,6 +589,8 @@ static void __init test_pointer(void) { plain(); null_pointer(); invalid_pointer(); symbol_ptr(); kernel_ptr(); struct_resource(); Loading
lib/vsprintf.c +271 −160 Original line number Diff line number Diff line Loading @@ -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) Loading @@ -614,8 +612,66 @@ char *string(char *buf, char *end, const char *s, struct printf_spec spec) 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 * 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 = error_string(*buf, end, err_msg, spec); return -EFAULT; } return 0; } static noinline_for_stack char *pointer_string(char *buf, char *end, const void *ptr, char *string(char *buf, char *end, const char *s, struct printf_spec spec) { if (check_pointer(&buf, end, s, spec)) return buf; return string_nocheck(buf, end, s, spec); } static char *pointer_string(char *buf, char *end, const void *ptr, struct printf_spec spec) { spec.base = 16; Loading Loading @@ -701,7 +757,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 error_string(buf, end, str, spec); } #ifdef CONFIG_64BIT Loading @@ -717,6 +773,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: /* Handle as %p, hash and do _not_ leak addresses. */ return ptr_to_id(buf, end, ptr, spec); 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 error_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) Loading @@ -736,6 +841,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) { Loading Loading @@ -766,8 +876,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])) { Loading Loading @@ -802,7 +916,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 Loading Loading @@ -886,29 +1000,32 @@ 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(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); Loading @@ -919,21 +1036,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 Loading @@ -948,9 +1065,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': Loading Loading @@ -997,6 +1113,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 }; Loading Loading @@ -1038,6 +1157,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; Loading Loading @@ -1076,6 +1198,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 = '-'; Loading @@ -1101,7 +1226,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 Loading Loading @@ -1264,7 +1389,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 Loading @@ -1275,7 +1400,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 Loading Loading @@ -1337,7 +1462,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 Loading Loading @@ -1372,7 +1497,42 @@ 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 char *ip_addr_string(char *buf, char *end, const void *ptr, struct printf_spec spec, const char *fmt) { 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); 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 error_string(buf, end, "(einval)", spec); }} } err_fmt_msg = fmt[0] == 'i' ? "(%pi?)" : "(%pI?)"; return error_string(buf, end, err_fmt_msg, spec); } static noinline_for_stack Loading @@ -1387,9 +1547,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++]) { Loading Loading @@ -1435,6 +1594,21 @@ 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, 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); return buf; } static noinline_for_stack char *uuid_string(char *buf, char *end, const u8 *addr, struct printf_spec spec, const char *fmt) Loading @@ -1445,6 +1619,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 */ Loading Loading @@ -1473,56 +1650,7 @@ char *uuid_string(char *buf, char *end, const u8 *addr, *p = 0; 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); return string_nocheck(buf, end, uuid, spec); } static noinline_for_stack Loading @@ -1532,24 +1660,31 @@ 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; size = sizeof(netdev_features_t); break; default: return ptr_to_id(buf, end, addr, spec); return error_string(buf, end, "(%pN?)", spec); } return special_hex_number(buf, end, num, size); } 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; Loading Loading @@ -1601,12 +1736,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; Loading Loading @@ -1640,9 +1779,9 @@ 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 ptr_to_id(buf, end, ptr, spec); return error_string(buf, end, "(%ptR?)", spec); } } Loading @@ -1650,8 +1789,11 @@ 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) return string(buf, end, NULL, spec); if (!IS_ENABLED(CONFIG_HAVE_CLK)) return error_string(buf, end, "(%pC?)", spec); if (check_pointer(&buf, end, clk, spec)) return buf; switch (fmt[1]) { case 'n': Loading @@ -1659,7 +1801,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 error_string(buf, end, "(%pC?)", spec); #endif } } Loading Loading @@ -1692,11 +1834,15 @@ 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; if (check_pointer(&buf, end, flags_ptr, spec)) return buf; switch (fmt[1]) { case 'p': flags = *(unsigned long *)flags_ptr; Loading @@ -1713,8 +1859,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 error_string(buf, end, "(%pG?)", spec); } return format_flags(buf, end, flags, names); Loading @@ -1736,13 +1881,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); } Loading Loading @@ -1770,10 +1915,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 error_string(buf, end, "(%pOF?)", spec); if ((unsigned long)dn < PAGE_SIZE) return string(buf, end, "(null)", spec); if (check_pointer(&buf, end, dn, spec)) return buf; /* simple case without anything any more format specifiers */ fmt++; Loading Loading @@ -1814,7 +1959,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); Loading @@ -1825,10 +1970,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; } Loading @@ -1841,6 +1986,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 error_string(buf, end, "(%pO?)", 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 Loading Loading @@ -1957,18 +2113,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(buf, end, "(null)", spec); } switch (*fmt) { case 'F': case 'f': Loading Loading @@ -2004,50 +2148,19 @@ 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(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': 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, spec, fmt); case 'K': if (!kptr_restrict) break; 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': Loading @@ -2064,13 +2177,9 @@ 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': 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); } Loading Loading @@ -2685,11 +2794,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); Loading