lib/vsprintf.c: improve sanity check in vsnprintf()
[pandora-kernel.git] / lib / vsprintf.c
index 993599e..77d1314 100644 (file)
@@ -25,6 +25,7 @@
 #include <linux/kallsyms.h>
 #include <linux/uaccess.h>
 #include <linux/ioport.h>
+#include <linux/cred.h>
 #include <net/addrconf.h>
 
 #include <asm/page.h>          /* for PAGE_SIZE */
@@ -886,15 +887,41 @@ char *pointer(const char *fmt, char *buf, char *end, void *ptr,
                 * %pK cannot be used in IRQ context because its test
                 * for CAP_SYSLOG would be meaningless.
                 */
-               if (in_irq() || in_serving_softirq() || in_nmi()) {
+               if (kptr_restrict && (in_irq() || in_serving_softirq() ||
+                                     in_nmi())) {
                        if (spec.field_width == -1)
                                spec.field_width = 2 * sizeof(void *);
                        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) ||
+                           cred->euid != cred->uid ||
+                           cred->egid != cred->gid)
+                               ptr = NULL;
+                       break;
+               }
+               case 2:
+               default:
+                       /* Always print 0's for %pK */
                        ptr = NULL;
+                       break;
+               }
                break;
        }
        spec.flags |= SMALL;
@@ -1150,7 +1177,7 @@ int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
 
        /* Reject out-of-range values early.  Large positive sizes are
           used for unknown buffer sizes. */
-       if (WARN_ON_ONCE((int) size < 0))
+       if (WARN_ON_ONCE(size > INT_MAX))
                return 0;
 
        str = buf;