return widen_string(buf, len, end, 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 = string_nocheck(*buf, end, err_msg, spec);
+               return -EFAULT;
+       }
+
+       return 0;
+}
+
 static noinline_for_stack
 char *string(char *buf, char *end, const char *s,
             struct printf_spec spec)
 {
-       if ((unsigned long)s < PAGE_SIZE)
-               s = "(null)";
+       if (check_pointer(&buf, end, s, spec))
+               return buf;
 
        return string_nocheck(buf, end, s, spec);
 }
 
        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) {
 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])) {
        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_nocheck(p, pend, "io  ", str_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':
        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 };
 
        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;
        char separator;
        bool reversed = false;
 
+       if (check_pointer(&buf, end, addr, spec))
+               return buf;
+
        switch (fmt[1]) {
        case 'F':
                separator = '-';
 {
        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);
        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++]) {
        return buf;
 }
 
-static char *va_format(char *buf, char *end, struct va_format *va_fmt)
+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);
        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 */
        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;
 }
 
 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;
 }
 
 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;
 {
        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 string_nocheck(buf, end, "(%ptR?)", spec);
        }
        if (!IS_ENABLED(CONFIG_HAVE_CLK))
                return string_nocheck(buf, end, "(%pC?)", spec);
 
-       if (!clk)
-               return string(buf, end, NULL, spec);
+       if (check_pointer(&buf, end, clk, spec))
+               return buf;
 
        switch (fmt[1]) {
        case 'n':
        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;
        if (!IS_ENABLED(CONFIG_OF))
                return string_nocheck(buf, end, "(%pOF?)", spec);
 
-       if ((unsigned long)dn < PAGE_SIZE)
-               return string_nocheck(buf, end, "(null)", spec);
+       if (check_pointer(&buf, end, dn, spec))
+               return buf;
 
        /* simple case without anything any more format specifiers */
        fmt++;
 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_nocheck(buf, end, "(null)", spec);
-       }
-
        switch (*fmt) {
        case 'F':
        case 'f':
        case 'U':
                return uuid_string(buf, end, ptr, spec, fmt);
        case 'V':
-               return va_format(buf, end, ptr);
+               return va_format(buf, end, ptr, spec, fmt);
        case 'K':
                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':
 
                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);