diff options
| author | Dave Weinstein <olorin@google.com> | 2017-01-11 15:39:07 -0800 |
|---|---|---|
| committer | Moyster <oysterized@gmail.com> | 2019-05-03 18:09:53 +0200 |
| commit | 2fe065dee79d9eb3525116a262083bc71a9b5c73 (patch) | |
| tree | 4c22139f6f48e4554f9af846ea42414054fbaa3b | |
| parent | 171a24bc4ba9e1aa4606303dc372799f20e74010 (diff) | |
ANDROID: lib: vsprintf: additional kernel pointer filtering options
Add the kptr_restrict setting of 3 which results in both
%p and %pK values being replaced by zeros.
Add an additional %pP value inspired by the Grsecurity
option which explicitly whitelists pointers for output.
This patch is based on work by William Roberts
<william.c.roberts@intel.com>
[CV: fixed GCC warning on 32 bit targets]
BUG: 30368199
Change-Id: Ic5cef86617f7758514271edd67199683d2c4e2bb
Signed-off-by: Dave Weinstein <olorin@google.com>
| -rw-r--r-- | Documentation/printk-formats.txt | 5 | ||||
| -rw-r--r-- | Documentation/sysctl/kernel.txt | 3 | ||||
| -rw-r--r-- | kernel/sysctl.c | 2 | ||||
| -rw-r--r-- | lib/vsprintf.c | 105 |
4 files changed, 76 insertions, 39 deletions
diff --git a/Documentation/printk-formats.txt b/Documentation/printk-formats.txt index 230153498..cd9798fa2 100644 --- a/Documentation/printk-formats.txt +++ b/Documentation/printk-formats.txt @@ -45,6 +45,11 @@ Kernel Pointers: users. The behaviour of %pK depends on the kptr_restrict sysctl - see Documentation/sysctl/kernel.txt for more details. + %pP 0x01234567 or 0x0123456789abcdef + + For printing kernel pointers which should always be shown, even to + unprivileged users. + Struct Resources: %pr [mem 0x60000000-0x6fffffff flags 0x2200] or diff --git a/Documentation/sysctl/kernel.txt b/Documentation/sysctl/kernel.txt index a8e65f559..3c28e6436 100644 --- a/Documentation/sysctl/kernel.txt +++ b/Documentation/sysctl/kernel.txt @@ -309,6 +309,9 @@ values to unprivileged users is a concern. When kptr_restrict is set to (2), kernel pointers printed using %pK will be replaced with 0's regardless of privileges. +When kptr_restrict is set to (3), kernel pointers printed using +%p and %pK will be replaced with 0's regardless of privileges. + ============================================================== kstack_depth_to_print: (X86 only) diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 63ad2ac67..2555c81d9 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -788,7 +788,7 @@ static struct ctl_table kern_table[] = { .mode = 0644, .proc_handler = proc_dointvec_minmax_sysadmin, .extra1 = &zero, - .extra2 = &two, + .extra2 = &three, }, #endif { diff --git a/lib/vsprintf.c b/lib/vsprintf.c index 1010dff5c..1f480996c 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -385,6 +385,14 @@ struct printf_spec { s16 precision; /* # of digits/chars */ }; +/* + * Always cleanse %p and %pK specifiers + */ +static inline int kptr_restrict_always_cleanse_pointers(void) +{ + return kptr_restrict >= 3; +} + static noinline_for_stack char *number(char *buf, char *end, unsigned long long num, struct printf_spec spec) @@ -1233,6 +1241,7 @@ int kptr_restrict __read_mostly = 4; * Do not use this feature without some mechanism to verify the * correctness of the format string and va_list arguments. * - 'K' For a kernel pointer that should be hidden from unprivileged users + * - 'P' For a kernel pointer that should be shown to all users * - 'NF' For a netdev_features_t * - 'h[CDN]' For a variable-length buffer, it prints it as a hex string with * a certain separator (' ' by default): @@ -1249,6 +1258,9 @@ int kptr_restrict __read_mostly = 4; * Note: The difference between 'S' and 'F' is that on ia64 and ppc64 * function pointers are really function descriptors, which contain a * pointer to the real address. + * + * Note: That for kptr_restrict set to 3, %p and %pK have the same + * meaning. */ static noinline_for_stack char *pointer(const char *fmt, char *buf, char *end, void *ptr, @@ -1256,7 +1268,7 @@ char *pointer(const char *fmt, char *buf, char *end, void *ptr, { const int default_width = 2 * sizeof(void *); - if (!ptr && *fmt != 'K') { + if (!ptr && *fmt != 'K' && !kptr_restrict_always_cleanse_pointers()) { /* * Print (null) with the same width as a pointer so it makes * tabular output look nice. @@ -1320,56 +1332,73 @@ char *pointer(const char *fmt, char *buf, char *end, void *ptr, va_end(va); return buf; } - case 'K': + case 'N': + switch (fmt[1]) { + case 'F': + return netdev_feature_string(buf, end, ptr, spec); + } + break; + case 'a': + spec.flags |= SPECIAL | SMALL | ZEROPAD; + spec.field_width = sizeof(phys_addr_t) * 2 + 2; + spec.base = 16; + return number(buf, end, + (unsigned long long) *((phys_addr_t *)ptr), spec); + case 'P': + /* + * an explicitly whitelisted kernel pointer should never be + * cleansed + */ + break; + default: /* - * %pK cannot be used in IRQ context because its test - * for CAP_SYSLOG would be meaningless. + * plain %p, no extension, check if we should always cleanse and + * treat like %pK. */ - if (kptr_restrict && (in_irq() || in_serving_softirq() || - in_nmi())) { - if (spec.field_width == -1) - spec.field_width = default_width; - return string(buf, end, "pK-error", spec); + if (!kptr_restrict_always_cleanse_pointers()) { + break; } switch (kptr_restrict) { case 0: - /* Always print %pK values */ + /* Always print %p values */ break; case 1: { - /* - * 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. - */ - const struct cred *cred = current_cred(); + 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 = default_width; + return string(buf, end, "pK-error", spec); + } - if (!has_capability_noaudit(current, CAP_SYSLOG) || - !uid_eq(cred->euid, cred->uid) || - !gid_eq(cred->egid, cred->gid)) - ptr = NULL; - break; - } - case 2: + /* + * 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 %p + * 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: /* restrict only %pK */ + case 3: /* restrict all non-extensioned %p and %pK */ default: - /* Always print 0's for %pK */ ptr = NULL; break; } break; - - case 'N': - switch (fmt[1]) { - case 'F': - return netdev_feature_string(buf, end, ptr, spec); - } - break; - case 'a': - return address_val(buf, end, ptr, spec, fmt); case 'd': return dentry_name(buf, end, ptr, spec, fmt); case 'D': @@ -1384,7 +1413,7 @@ char *pointer(const char *fmt, char *buf, char *end, void *ptr, } spec.base = 16; - return number(buf, end, (unsigned long) ptr, spec); + return number(buf, end, (unsigned long long) (uintptr_t) ptr, spec); } /* |
