aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDave Weinstein <olorin@google.com>2017-01-11 15:39:07 -0800
committerMoyster <oysterized@gmail.com>2019-05-03 18:09:53 +0200
commit2fe065dee79d9eb3525116a262083bc71a9b5c73 (patch)
tree4c22139f6f48e4554f9af846ea42414054fbaa3b
parent171a24bc4ba9e1aa4606303dc372799f20e74010 (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.txt5
-rw-r--r--Documentation/sysctl/kernel.txt3
-rw-r--r--kernel/sysctl.c2
-rw-r--r--lib/vsprintf.c105
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);
}
/*