mirror of
https://github.com/edk2-porting/linux-next.git
synced 2024-12-18 02:04:05 +08:00
Merge branch 'for-5.2-vsprintf-hardening' into for-linus
This commit is contained in:
commit
35e1547511
@ -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
|
||||
--------------
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
@ -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);
|
||||
@ -572,6 +589,8 @@ static void __init
|
||||
test_pointer(void)
|
||||
{
|
||||
plain();
|
||||
null_pointer();
|
||||
invalid_pointer();
|
||||
symbol_ptr();
|
||||
kernel_ptr();
|
||||
struct_resource();
|
||||
|
431
lib/vsprintf.c
431
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)
|
||||
@ -614,9 +612,67 @@ 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,
|
||||
struct printf_spec spec)
|
||||
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;
|
||||
spec.flags |= SMALL;
|
||||
@ -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
|
||||
@ -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)
|
||||
@ -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) {
|
||||
@ -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])) {
|
||||
@ -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
|
||||
@ -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);
|
||||
@ -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
|
||||
@ -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':
|
||||
@ -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 };
|
||||
|
||||
@ -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;
|
||||
@ -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 = '-';
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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++]) {
|
||||
@ -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)
|
||||
@ -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 */
|
||||
@ -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
|
||||
@ -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;
|
||||
@ -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;
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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':
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
@ -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);
|
||||
@ -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);
|
||||
}
|
||||
@ -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++;
|
||||
@ -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);
|
||||
@ -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;
|
||||
}
|
||||
@ -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
|
||||
@ -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':
|
||||
@ -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':
|
||||
@ -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);
|
||||
}
|
||||
@ -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…
Reference in New Issue
Block a user