vsprintf: check real user/group id for %pK
[linux-3.10.git] / lib / vsprintf.c
index e149c64..620fae4 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/math64.h>
 #include <linux/uaccess.h>
 #include <linux/ioport.h>
+#include <linux/cred.h>
 #include <net/addrconf.h>
 
 #include <asm/page.h>          /* for PAGE_SIZE */
@@ -1118,11 +1119,37 @@ char *pointer(const char *fmt, char *buf, char *end, void *ptr,
                                spec.field_width = default_width;
                        return string(buf, end, "pK-error", spec);
                }
-               if (!((kptr_restrict == 0) ||
-                     (kptr_restrict == 1 &&
-                      has_capability_noaudit(current, CAP_SYSLOG))))
+
+               switch (kptr_restrict) {
+               case 0:
+                       /* Always print %pK 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();
+
+                       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;
+               }
                break;
+
        case 'N':
                switch (fmt[1]) {
                case 'F':