summaryrefslogtreecommitdiff
path: root/lib/vsprintf.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/vsprintf.c')
-rw-r--r--lib/vsprintf.c1260
1 files changed, 733 insertions, 527 deletions
diff --git a/lib/vsprintf.c b/lib/vsprintf.c
index 259e55895933..a3790c43a0ab 100644
--- a/lib/vsprintf.c
+++ b/lib/vsprintf.c
@@ -17,7 +17,7 @@
* - scnprintf and vscnprintf
*/
-#include <stdarg.h>
+#include <linux/stdarg.h>
#include <linux/build_bug.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
@@ -34,6 +34,7 @@
#include <linux/dcache.h>
#include <linux/cred.h>
#include <linux/rtc.h>
+#include <linux/sprintf.h>
#include <linux/time.h>
#include <linux/uuid.h>
#include <linux/of.h>
@@ -41,6 +42,7 @@
#include <linux/siphash.h>
#include <linux/compiler.h>
#include <linux/property.h>
+#include <linux/notifier.h>
#ifdef CONFIG_BLOCK
#include <linux/blkdev.h>
#endif
@@ -49,33 +51,67 @@
#include <asm/page.h> /* for PAGE_SIZE */
#include <asm/byteorder.h> /* cpu_to_le16 */
+#include <linux/unaligned.h>
#include <linux/string_helpers.h>
#include "kstrtox.h"
-/**
- * simple_strtoull - convert a string to an unsigned long long
- * @cp: The start of the string
- * @endp: A pointer to the end of the parsed string will be placed here
- * @base: The number base to use
+/* Disable pointer hashing if requested */
+bool no_hash_pointers __ro_after_init;
+EXPORT_SYMBOL_GPL(no_hash_pointers);
+
+/*
+ * Hashed pointers policy selected by "hash_pointers=..." boot param
*
- * This function has caveats. Please use kstrtoull instead.
+ * `auto` - Hashed pointers enabled unless disabled by slub_debug_enabled=true
+ * `always` - Hashed pointers enabled unconditionally
+ * `never` - Hashed pointers disabled unconditionally
*/
-unsigned long long simple_strtoull(const char *cp, char **endp, unsigned int base)
+enum hash_pointers_policy {
+ HASH_PTR_AUTO = 0,
+ HASH_PTR_ALWAYS,
+ HASH_PTR_NEVER
+};
+static enum hash_pointers_policy hash_pointers_mode __initdata;
+
+noinline
+static unsigned long long simple_strntoull(const char *startp, char **endp, unsigned int base, size_t max_chars)
{
- unsigned long long result;
+ const char *cp;
+ unsigned long long result = 0ULL;
+ size_t prefix_chars;
unsigned int rv;
- cp = _parse_integer_fixup_radix(cp, &base);
- rv = _parse_integer(cp, base, &result);
- /* FIXME */
- cp += (rv & ~KSTRTOX_OVERFLOW);
+ cp = _parse_integer_fixup_radix(startp, &base);
+ prefix_chars = cp - startp;
+ if (prefix_chars < max_chars) {
+ rv = _parse_integer_limit(cp, base, &result, max_chars - prefix_chars);
+ /* FIXME */
+ cp += (rv & ~KSTRTOX_OVERFLOW);
+ } else {
+ /* Field too short for prefix + digit, skip over without converting */
+ cp = startp + max_chars;
+ }
if (endp)
*endp = (char *)cp;
return result;
}
+
+/**
+ * simple_strtoull - convert a string to an unsigned long long
+ * @cp: The start of the string
+ * @endp: A pointer to the end of the parsed string will be placed here
+ * @base: The number base to use
+ *
+ * This function has caveats. Please use kstrtoull instead.
+ */
+noinline
+unsigned long long simple_strtoull(const char *cp, char **endp, unsigned int base)
+{
+ return simple_strntoull(cp, endp, base, INT_MAX);
+}
EXPORT_SYMBOL(simple_strtoull);
/**
@@ -92,6 +128,13 @@ unsigned long simple_strtoul(const char *cp, char **endp, unsigned int base)
}
EXPORT_SYMBOL(simple_strtoul);
+unsigned long simple_strntoul(const char *cp, char **endp, unsigned int base,
+ size_t max_chars)
+{
+ return simple_strntoull(cp, endp, base, max_chars);
+}
+EXPORT_SYMBOL(simple_strntoul);
+
/**
* simple_strtol - convert a string to a signed long
* @cp: The start of the string
@@ -109,6 +152,21 @@ long simple_strtol(const char *cp, char **endp, unsigned int base)
}
EXPORT_SYMBOL(simple_strtol);
+noinline
+static long long simple_strntoll(const char *cp, char **endp, unsigned int base, size_t max_chars)
+{
+ /*
+ * simple_strntoull() safely handles receiving max_chars==0 in the
+ * case cp[0] == '-' && max_chars == 1.
+ * If max_chars == 0 we can drop through and pass it to simple_strntoull()
+ * and the content of *cp is irrelevant.
+ */
+ if (*cp == '-' && max_chars > 0)
+ return -simple_strntoull(cp + 1, endp, base, max_chars - 1);
+
+ return simple_strntoull(cp, endp, base, max_chars);
+}
+
/**
* simple_strtoll - convert a string to a signed long long
* @cp: The start of the string
@@ -119,15 +177,11 @@ EXPORT_SYMBOL(simple_strtol);
*/
long long simple_strtoll(const char *cp, char **endp, unsigned int base)
{
- if (*cp == '-')
- return -simple_strtoull(cp + 1, endp, base);
-
- return simple_strtoull(cp, endp, base);
+ return simple_strntoll(cp, endp, base, INT_MAX);
}
EXPORT_SYMBOL(simple_strtoll);
-static noinline_for_stack
-int skip_atoi(const char **s)
+static inline int skip_atoi(const char **s)
{
int i = 0;
@@ -373,7 +427,7 @@ int num_to_str(char *buf, int size, unsigned long long num, unsigned int width)
return len + width;
}
-#define SIGN 1 /* unsigned/signed, must be 1 */
+#define SIGN 1 /* unsigned/signed */
#define LEFT 2 /* left justified */
#define PLUS 4 /* show plus */
#define SPACE 8 /* space if plus */
@@ -381,34 +435,26 @@ int num_to_str(char *buf, int size, unsigned long long num, unsigned int width)
#define SMALL 32 /* use lowercase in hex (must be 32 == 0x20) */
#define SPECIAL 64 /* prefix hex with "0x", octal with "0" */
-enum format_type {
- FORMAT_TYPE_NONE, /* Just a string part */
- FORMAT_TYPE_WIDTH,
- FORMAT_TYPE_PRECISION,
- FORMAT_TYPE_CHAR,
- FORMAT_TYPE_STR,
- FORMAT_TYPE_PTR,
- FORMAT_TYPE_PERCENT_CHAR,
- FORMAT_TYPE_INVALID,
- FORMAT_TYPE_LONG_LONG,
- FORMAT_TYPE_ULONG,
- FORMAT_TYPE_LONG,
- FORMAT_TYPE_UBYTE,
- FORMAT_TYPE_BYTE,
- FORMAT_TYPE_USHORT,
- FORMAT_TYPE_SHORT,
- FORMAT_TYPE_UINT,
- FORMAT_TYPE_INT,
- FORMAT_TYPE_SIZE_T,
- FORMAT_TYPE_PTRDIFF
+static_assert(ZEROPAD == ('0' - ' '));
+static_assert(SMALL == ('a' ^ 'A'));
+
+enum format_state {
+ FORMAT_STATE_NONE, /* Just a string part */
+ FORMAT_STATE_NUM,
+ FORMAT_STATE_WIDTH,
+ FORMAT_STATE_PRECISION,
+ FORMAT_STATE_CHAR,
+ FORMAT_STATE_STR,
+ FORMAT_STATE_PTR,
+ FORMAT_STATE_PERCENT_CHAR,
+ FORMAT_STATE_INVALID,
};
struct printf_spec {
- unsigned int type:8; /* format_type enum */
- signed int field_width:24; /* width of output field */
- unsigned int flags:8; /* flags to number() */
- unsigned int base:8; /* number base, 8, 10 or 16 only */
- signed int precision:16; /* # of digits/chars */
+ unsigned char flags; /* flags to number() */
+ unsigned char base; /* number base, 8, 10 or 16 only */
+ short precision; /* # of digits/chars */
+ int field_width; /* width of output field */
} __packed;
static_assert(sizeof(struct printf_spec) == 8);
@@ -507,7 +553,7 @@ char *number(char *buf, char *end, unsigned long long num,
/* zero or space padding */
if (!(spec.flags & LEFT)) {
char c = ' ' + (spec.flags & ZEROPAD);
- BUILD_BUG_ON(' ' + ZEROPAD != '0');
+
while (--field_width >= 0) {
if (buf < end)
*buf = c;
@@ -536,18 +582,18 @@ char *number(char *buf, char *end, unsigned long long num,
return buf;
}
+#define special_hex_spec(size) \
+(struct printf_spec) { \
+ .field_width = 2 + 2 * (size), /* 0x + hex */ \
+ .flags = SPECIAL | SMALL | ZEROPAD, \
+ .base = 16, \
+ .precision = -1, \
+}
+
static noinline_for_stack
char *special_hex_number(char *buf, char *end, unsigned long long num, int size)
{
- struct printf_spec spec;
-
- spec.type = FORMAT_TYPE_PTR;
- spec.field_width = 2 + 2 * size; /* 0x + hex */
- spec.flags = SPECIAL | SMALL | ZEROPAD;
- spec.base = 16;
- spec.precision = -1;
-
- return number(buf, end, num, spec);
+ return number(buf, end, num, special_hex_spec(size));
}
static void move_right(char *buf, char *end, unsigned len, unsigned spaces)
@@ -715,59 +761,37 @@ static int __init debug_boot_weak_hash_enable(char *str)
}
early_param("debug_boot_weak_hash", debug_boot_weak_hash_enable);
-static DEFINE_STATIC_KEY_TRUE(not_filled_random_ptr_key);
+static bool filled_random_ptr_key __read_mostly;
static siphash_key_t ptr_key __read_mostly;
-static void enable_ptr_key_workfn(struct work_struct *work)
+static int fill_ptr_key(struct notifier_block *nb, unsigned long action, void *data)
{
get_random_bytes(&ptr_key, sizeof(ptr_key));
- /* Needs to run from preemptible context */
- static_branch_disable(&not_filled_random_ptr_key);
-}
-
-static DECLARE_WORK(enable_ptr_key_work, enable_ptr_key_workfn);
-static void fill_random_ptr_key(struct random_ready_callback *unused)
-{
- /* This may be in an interrupt handler. */
- queue_work(system_unbound_wq, &enable_ptr_key_work);
+ /* Pairs with smp_rmb() before reading ptr_key. */
+ smp_wmb();
+ WRITE_ONCE(filled_random_ptr_key, true);
+ return NOTIFY_DONE;
}
-static struct random_ready_callback random_ready = {
- .func = fill_random_ptr_key
-};
-
-static int __init initialize_ptr_random(void)
+static int __init vsprintf_init_hashval(void)
{
- int key_size = sizeof(ptr_key);
- int ret;
-
- /* Use hw RNG if available. */
- if (get_random_bytes_arch(&ptr_key, key_size) == key_size) {
- static_branch_disable(&not_filled_random_ptr_key);
- return 0;
- }
-
- ret = add_random_ready_callback(&random_ready);
- if (!ret) {
- return 0;
- } else if (ret == -EALREADY) {
- /* This is in preemptible context */
- enable_ptr_key_workfn(&enable_ptr_key_work);
- return 0;
- }
-
- return ret;
+ static struct notifier_block fill_ptr_key_nb = { .notifier_call = fill_ptr_key };
+ execute_with_initialized_rng(&fill_ptr_key_nb);
+ return 0;
}
-early_initcall(initialize_ptr_random);
+subsys_initcall(vsprintf_init_hashval)
/* Maps a pointer to a 32 bit unique identifier. */
static inline int __ptr_to_hashval(const void *ptr, unsigned long *hashval_out)
{
unsigned long hashval;
- if (static_branch_unlikely(&not_filled_random_ptr_key))
- return -EAGAIN;
+ if (!READ_ONCE(filled_random_ptr_key))
+ return -EBUSY;
+
+ /* Pairs with smp_wmb() after writing ptr_key. */
+ smp_rmb();
#ifdef CONFIG_64BIT
hashval = (unsigned long)siphash_1u64((u64)ptr, &ptr_key);
@@ -818,6 +842,19 @@ static char *ptr_to_id(char *buf, char *end, const void *ptr,
return pointer_string(buf, end, (const void *)hashval, spec);
}
+static char *default_pointer(char *buf, char *end, const void *ptr,
+ struct printf_spec spec)
+{
+ /*
+ * default is to _not_ leak addresses, so hash before printing,
+ * unless no_hash_pointers is specified on the command line.
+ */
+ if (unlikely(no_hash_pointers))
+ return pointer_string(buf, end, ptr, spec);
+
+ return ptr_to_id(buf, end, ptr, spec);
+}
+
int kptr_restrict __read_mostly;
static noinline_for_stack
@@ -827,7 +864,7 @@ char *restricted_pointer(char *buf, char *end, const void *ptr,
switch (kptr_restrict) {
case 0:
/* Handle as %p, hash and do _not_ leak addresses. */
- return ptr_to_id(buf, end, ptr, spec);
+ return default_pointer(buf, end, ptr, spec);
case 1: {
const struct cred *cred;
@@ -835,7 +872,7 @@ char *restricted_pointer(char *buf, char *end, const void *ptr,
* 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 (in_hardirq() || in_serving_softirq() || in_nmi()) {
if (spec.field_width == -1)
spec.field_width = 2 * sizeof(ptr);
return error_string(buf, end, "pK-error", spec);
@@ -937,13 +974,13 @@ char *bdev_name(char *buf, char *end, struct block_device *bdev,
hd = bdev->bd_disk;
buf = string(buf, end, hd->disk_name, spec);
- if (bdev->bd_part->partno) {
+ if (bdev_is_partition(bdev)) {
if (isdigit(hd->disk_name[strlen(hd->disk_name)-1])) {
if (buf < end)
*buf = 'p';
buf++;
}
- buf = number(buf, end, bdev->bd_part->partno, spec);
+ buf = number(buf, end, bdev_partno(bdev), spec);
}
return buf;
}
@@ -963,8 +1000,12 @@ char *symbol_string(char *buf, char *end, void *ptr,
value = (unsigned long)ptr;
#ifdef CONFIG_KALLSYMS
- if (*fmt == 'B')
+ if (*fmt == 'B' && fmt[1] == 'b')
+ sprint_backtrace_build_id(sym, value);
+ else if (*fmt == 'B')
sprint_backtrace(sym, value);
+ else if (*fmt == 'S' && (fmt[1] == 'b' || (fmt[1] == 'R' && fmt[2] == 'b')))
+ sprint_symbol_build_id(sym, value);
else if (*fmt != 's')
sprint_symbol(sym, value);
else
@@ -1007,6 +1048,20 @@ static const struct printf_spec default_dec04_spec = {
};
static noinline_for_stack
+char *hex_range(char *buf, char *end, u64 start_val, u64 end_val,
+ struct printf_spec spec)
+{
+ buf = number(buf, end, start_val, spec);
+ if (start_val == end_val)
+ return buf;
+
+ if (buf < end)
+ *buf = '-';
+ ++buf;
+ return number(buf, end, end_val, spec);
+}
+
+static noinline_for_stack
char *resource_string(char *buf, char *end, struct resource *res,
struct printf_spec spec, const char *fmt)
{
@@ -1047,7 +1102,7 @@ char *resource_string(char *buf, char *end, struct resource *res,
#define FLAG_BUF_SIZE (2 * sizeof(res->flags))
#define DECODED_BUF_SIZE sizeof("[mem - 64bit pref window disabled]")
#define RAW_BUF_SIZE sizeof("[mem - flags 0x]")
- char sym[max(2*RSRC_BUF_SIZE + DECODED_BUF_SIZE,
+ char sym[MAX(2*RSRC_BUF_SIZE + DECODED_BUF_SIZE,
2*RSRC_BUF_SIZE + FLAG_BUF_SIZE + RAW_BUF_SIZE)];
char *p = sym, *pend = sym + sizeof(sym);
@@ -1082,11 +1137,7 @@ char *resource_string(char *buf, char *end, struct resource *res,
p = string_nocheck(p, pend, "size ", str_spec);
p = number(p, pend, resource_size(res), *specp);
} else {
- p = number(p, pend, res->start, *specp);
- if (res->start != res->end) {
- *p++ = '-';
- p = number(p, pend, res->end, *specp);
- }
+ p = hex_range(p, pend, res->start, res->end, *specp);
}
if (decode) {
if (res->flags & IORESOURCE_MEM_64)
@@ -1108,6 +1159,24 @@ char *resource_string(char *buf, char *end, struct resource *res,
}
static noinline_for_stack
+char *range_string(char *buf, char *end, const struct range *range,
+ struct printf_spec spec, const char *fmt)
+{
+ char sym[sizeof("[range 0x0123456789abcdef-0x0123456789abcdef]")];
+ char *p = sym, *pend = sym + sizeof(sym);
+
+ if (check_pointer(&buf, end, range, spec))
+ return buf;
+
+ p = string_nocheck(p, pend, "[range ", default_str_spec);
+ p = hex_range(p, pend, range->start, range->end, special_hex_spec(sizeof(range->start)));
+ *p++ = ']';
+ *p = '\0';
+
+ return string_nocheck(buf, end, sym, spec);
+}
+
+static noinline_for_stack
char *hex_string(char *buf, char *end, u8 *addr, struct printf_spec spec,
const char *fmt)
{
@@ -1159,7 +1228,7 @@ char *hex_string(char *buf, char *end, u8 *addr, struct printf_spec spec,
}
static noinline_for_stack
-char *bitmap_string(char *buf, char *end, unsigned long *bitmap,
+char *bitmap_string(char *buf, char *end, const unsigned long *bitmap,
struct printf_spec spec, const char *fmt)
{
const int CHUNKSZ = 32;
@@ -1203,24 +1272,17 @@ char *bitmap_string(char *buf, char *end, unsigned long *bitmap,
}
static noinline_for_stack
-char *bitmap_list_string(char *buf, char *end, unsigned long *bitmap,
+char *bitmap_list_string(char *buf, char *end, const unsigned long *bitmap,
struct printf_spec spec, const char *fmt)
{
int nr_bits = max_t(int, spec.field_width, 0);
- /* current bit is 'cur', most recently seen range is [rbot, rtop] */
- int cur, rbot, rtop;
bool first = true;
+ int rbot, rtop;
if (check_pointer(&buf, end, bitmap, spec))
return buf;
- rbot = cur = find_first_bit(bitmap, nr_bits);
- while (cur < nr_bits) {
- rtop = cur;
- cur = find_next_bit(bitmap, nr_bits, cur + 1);
- if (cur < nr_bits && cur <= rtop + 1)
- continue;
-
+ for_each_set_bitrange(rbot, rtop, bitmap, nr_bits) {
if (!first) {
if (buf < end)
*buf = ',';
@@ -1229,15 +1291,12 @@ char *bitmap_list_string(char *buf, char *end, unsigned long *bitmap,
first = false;
buf = number(buf, end, rbot, default_dec_spec);
- if (rbot < rtop) {
- if (buf < end)
- *buf = '-';
- buf++;
-
- buf = number(buf, end, rtop, default_dec_spec);
- }
+ if (rtop == rbot + 1)
+ continue;
- rbot = cur;
+ if (buf < end)
+ *buf = '-';
+ buf = number(++buf, end, rtop - 1, default_dec_spec);
}
return buf;
}
@@ -1262,7 +1321,7 @@ char *mac_address_string(char *buf, char *end, u8 *addr,
case 'R':
reversed = true;
- /* fall through */
+ fallthrough;
default:
separator = ':';
@@ -1648,8 +1707,11 @@ char *escaped_string(char *buf, char *end, u8 *addr, struct printf_spec spec,
return buf;
}
+__diag_push();
+__diag_ignore(GCC, all, "-Wsuggest-attribute=format",
+ "Not a valid __printf() conversion candidate.");
static char *va_format(char *buf, char *end, struct va_format *va_fmt,
- struct printf_spec spec, const char *fmt)
+ struct printf_spec spec)
{
va_list va;
@@ -1662,6 +1724,7 @@ static char *va_format(char *buf, char *end, struct va_format *va_fmt,
return buf;
}
+__diag_pop();
static noinline_for_stack
char *uuid_string(char *buf, char *end, const u8 *addr,
@@ -1678,7 +1741,8 @@ char *uuid_string(char *buf, char *end, const u8 *addr,
switch (*(++fmt)) {
case 'L':
- uc = true; /* fall-through */
+ uc = true;
+ fallthrough;
case 'l':
index = guid_index;
break;
@@ -1730,6 +1794,66 @@ char *netdev_bits(char *buf, char *end, const void *addr,
}
static noinline_for_stack
+char *fourcc_string(char *buf, char *end, const u32 *fourcc,
+ struct printf_spec spec, const char *fmt)
+{
+ char output[sizeof("0123 little-endian (0x01234567)")];
+ char *p = output;
+ unsigned int i;
+ bool pixel_fmt = false;
+ u32 orig, val;
+
+ if (fmt[1] != 'c')
+ return error_string(buf, end, "(%p4?)", spec);
+
+ if (check_pointer(&buf, end, fourcc, spec))
+ return buf;
+
+ orig = get_unaligned(fourcc);
+ switch (fmt[2]) {
+ case 'h':
+ if (fmt[3] == 'R')
+ orig = swab32(orig);
+ break;
+ case 'l':
+ orig = (__force u32)cpu_to_le32(orig);
+ break;
+ case 'b':
+ orig = (__force u32)cpu_to_be32(orig);
+ break;
+ case 'c':
+ /* Pixel formats are printed LSB-first */
+ pixel_fmt = true;
+ break;
+ default:
+ return error_string(buf, end, "(%p4?)", spec);
+ }
+
+ val = pixel_fmt ? swab32(orig & ~BIT(31)) : orig;
+
+ for (i = 0; i < sizeof(u32); i++) {
+ unsigned char c = val >> ((3 - i) * 8);
+
+ /* Print non-control ASCII characters as-is, dot otherwise */
+ *p++ = isascii(c) && isprint(c) ? c : '.';
+ }
+
+ if (pixel_fmt) {
+ *p++ = ' ';
+ strcpy(p, orig & BIT(31) ? "big-endian" : "little-endian");
+ p += strlen(p);
+ }
+
+ *p++ = ' ';
+ *p++ = '(';
+ p = special_hex_number(p, output + sizeof(output) - 2, orig, sizeof(u32));
+ *p++ = ')';
+ *p = '\0';
+
+ return string(buf, end, output, spec);
+}
+
+static noinline_for_stack
char *address_val(char *buf, char *end, const void *addr,
struct printf_spec spec, const char *fmt)
{
@@ -1794,12 +1918,10 @@ 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;
+ bool raw = false, iso8601_separator = true;
+ bool found = true;
int count = 2;
- if (check_pointer(&buf, end, tm, spec))
- return buf;
-
switch (fmt[count]) {
case 'd':
have_t = false;
@@ -1811,14 +1933,25 @@ char *rtc_str(char *buf, char *end, const struct rtc_time *tm,
break;
}
- raw = fmt[count] == 'r';
+ do {
+ switch (fmt[count++]) {
+ case 'r':
+ raw = true;
+ break;
+ case 's':
+ iso8601_separator = false;
+ break;
+ default:
+ found = false;
+ break;
+ }
+ } while (found);
if (have_d)
buf = date_str(buf, end, tm, raw);
if (have_d && have_t) {
- /* Respect ISO 8601 */
if (buf < end)
- *buf = 'T';
+ *buf = iso8601_separator ? 'T' : ' ';
buf++;
}
if (have_t)
@@ -1851,12 +1984,39 @@ char *time64_str(char *buf, char *end, const time64_t time,
}
static noinline_for_stack
+char *timespec64_str(char *buf, char *end, const struct timespec64 *ts,
+ struct printf_spec spec, const char *fmt)
+{
+ static const struct printf_spec default_dec09_spec = {
+ .base = 10,
+ .field_width = 9,
+ .precision = -1,
+ .flags = ZEROPAD,
+ };
+
+ if (fmt[2] == 'p')
+ buf = number(buf, end, ts->tv_sec, default_dec_spec);
+ else
+ buf = time64_str(buf, end, ts->tv_sec, spec, fmt);
+ if (buf < end)
+ *buf = '.';
+ buf++;
+
+ return number(buf, end, ts->tv_nsec, default_dec09_spec);
+}
+
+static noinline_for_stack
char *time_and_date(char *buf, char *end, void *ptr, struct printf_spec spec,
const char *fmt)
{
+ if (check_pointer(&buf, end, ptr, spec))
+ return buf;
+
switch (fmt[1]) {
case 'R':
return rtc_str(buf, end, (const struct rtc_time *)ptr, spec, fmt);
+ case 'S':
+ return timespec64_str(buf, end, (const struct timespec64 *)ptr, spec, fmt);
case 'T':
return time64_str(buf, end, *(const time64_t *)ptr, spec, fmt);
default:
@@ -1874,15 +2034,11 @@ char *clock(char *buf, char *end, struct clk *clk, struct printf_spec spec,
if (check_pointer(&buf, end, clk, spec))
return buf;
- switch (fmt[1]) {
- case 'n':
- default:
#ifdef CONFIG_COMMON_CLK
- return string(buf, end, __clk_get_name(clk), spec);
+ return string(buf, end, __clk_get_name(clk), spec);
#else
- return ptr_to_id(buf, end, clk, spec);
+ return ptr_to_id(buf, end, clk, spec);
#endif
- }
}
static
@@ -1912,6 +2068,74 @@ char *format_flags(char *buf, char *end, unsigned long flags,
return buf;
}
+struct page_flags_fields {
+ int width;
+ int shift;
+ int mask;
+ const struct printf_spec *spec;
+ const char *name;
+};
+
+static const struct page_flags_fields pff[] = {
+ {SECTIONS_WIDTH, SECTIONS_PGSHIFT, SECTIONS_MASK,
+ &default_dec_spec, "section"},
+ {NODES_WIDTH, NODES_PGSHIFT, NODES_MASK,
+ &default_dec_spec, "node"},
+ {ZONES_WIDTH, ZONES_PGSHIFT, ZONES_MASK,
+ &default_dec_spec, "zone"},
+ {LAST_CPUPID_WIDTH, LAST_CPUPID_PGSHIFT, LAST_CPUPID_MASK,
+ &default_flag_spec, "lastcpupid"},
+ {KASAN_TAG_WIDTH, KASAN_TAG_PGSHIFT, KASAN_TAG_MASK,
+ &default_flag_spec, "kasantag"},
+};
+
+static
+char *format_page_flags(char *buf, char *end, unsigned long flags)
+{
+ unsigned long main_flags = flags & PAGEFLAGS_MASK;
+ bool append = false;
+ int i;
+
+ buf = number(buf, end, flags, default_flag_spec);
+ if (buf < end)
+ *buf = '(';
+ buf++;
+
+ /* Page flags from the main area. */
+ if (main_flags) {
+ buf = format_flags(buf, end, main_flags, pageflag_names);
+ append = true;
+ }
+
+ /* Page flags from the fields area */
+ for (i = 0; i < ARRAY_SIZE(pff); i++) {
+ /* Skip undefined fields. */
+ if (!pff[i].width)
+ continue;
+
+ /* Format: Flag Name + '=' (equals sign) + Number + '|' (separator) */
+ if (append) {
+ if (buf < end)
+ *buf = '|';
+ buf++;
+ }
+
+ buf = string(buf, end, pff[i].name, default_str_spec);
+ if (buf < end)
+ *buf = '=';
+ buf++;
+ buf = number(buf, end, (flags >> pff[i].shift) & pff[i].mask,
+ *pff[i].spec);
+
+ append = true;
+ }
+ if (buf < end)
+ *buf = ')';
+ buf++;
+
+ return buf;
+}
+
static noinline_for_stack
char *flags_string(char *buf, char *end, void *flags_ptr,
struct printf_spec spec, const char *fmt)
@@ -1924,17 +2148,13 @@ char *flags_string(char *buf, char *end, void *flags_ptr,
switch (fmt[1]) {
case 'p':
- flags = *(unsigned long *)flags_ptr;
- /* Remove zone id */
- flags &= (1UL << NR_PAGEFLAGS) - 1;
- names = pageflag_names;
- break;
+ return format_page_flags(buf, end, *(unsigned long *)flags_ptr);
case 'v':
flags = *(unsigned long *)flags_ptr;
names = vmaflag_names;
break;
case 'g':
- flags = *(gfp_t *)flags_ptr;
+ flags = (__force unsigned long)(*(gfp_t *)flags_ptr);
names = gfpflag_names;
break;
default:
@@ -1952,15 +2172,20 @@ char *fwnode_full_name_string(struct fwnode_handle *fwnode, char *buf,
/* Loop starting from the root node to the current node. */
for (depth = fwnode_count_parents(fwnode); depth >= 0; depth--) {
- struct fwnode_handle *__fwnode =
- fwnode_get_nth_parent(fwnode, depth);
+ /*
+ * Only get a reference for other nodes (i.e. parent nodes).
+ * fwnode refcount may be 0 here.
+ */
+ struct fwnode_handle *__fwnode = depth ?
+ fwnode_get_nth_parent(fwnode, depth) : fwnode;
buf = string(buf, end, fwnode_get_name_prefix(__fwnode),
default_str_spec);
buf = string(buf, end, fwnode_get_name(__fwnode),
default_str_spec);
- fwnode_handle_put(__fwnode);
+ if (depth)
+ fwnode_handle_put(__fwnode);
}
return buf;
@@ -1976,12 +2201,6 @@ char *device_node_string(char *buf, char *end, struct device_node *dn,
char *buf_start = buf;
struct property *prop;
bool has_mult, pass;
- static const struct printf_spec num_spec = {
- .flags = SMALL,
- .field_width = -1,
- .precision = -1,
- .base = 10,
- };
struct printf_spec str_spec = spec;
str_spec.field_width = -1;
@@ -2021,7 +2240,7 @@ char *device_node_string(char *buf, char *end, struct device_node *dn,
str_spec.precision = precision;
break;
case 'p': /* phandle */
- buf = number(buf, end, (unsigned int)dn->phandle, num_spec);
+ buf = number(buf, end, (unsigned int)dn->phandle, default_dec_spec);
break;
case 'P': /* path-spec */
p = fwnode_get_name(of_fwnode_handle(dn));
@@ -2092,6 +2311,79 @@ char *fwnode_string(char *buf, char *end, struct fwnode_handle *fwnode,
return widen_string(buf, buf - buf_start, end, spec);
}
+static noinline_for_stack
+char *resource_or_range(const char *fmt, char *buf, char *end, void *ptr,
+ struct printf_spec spec)
+{
+ if (*fmt == 'r' && fmt[1] == 'a')
+ return range_string(buf, end, ptr, spec, fmt);
+ return resource_string(buf, end, ptr, spec, fmt);
+}
+
+void __init hash_pointers_finalize(bool slub_debug)
+{
+ switch (hash_pointers_mode) {
+ case HASH_PTR_ALWAYS:
+ no_hash_pointers = false;
+ break;
+ case HASH_PTR_NEVER:
+ no_hash_pointers = true;
+ break;
+ case HASH_PTR_AUTO:
+ default:
+ no_hash_pointers = slub_debug;
+ break;
+ }
+
+ if (!no_hash_pointers)
+ return;
+
+ pr_warn("**********************************************************\n");
+ pr_warn("** NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE **\n");
+ pr_warn("** **\n");
+ pr_warn("** This system shows unhashed kernel memory addresses **\n");
+ pr_warn("** via the console, logs, and other interfaces. This **\n");
+ pr_warn("** might reduce the security of your system. **\n");
+ pr_warn("** **\n");
+ pr_warn("** If you see this message and you are not debugging **\n");
+ pr_warn("** the kernel, report this immediately to your system **\n");
+ pr_warn("** administrator! **\n");
+ pr_warn("** **\n");
+ pr_warn("** Use hash_pointers=always to force this mode off **\n");
+ pr_warn("** **\n");
+ pr_warn("** NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE **\n");
+ pr_warn("**********************************************************\n");
+}
+
+static int __init hash_pointers_mode_parse(char *str)
+{
+ if (!str) {
+ pr_warn("Hash pointers mode empty; falling back to auto.\n");
+ hash_pointers_mode = HASH_PTR_AUTO;
+ } else if (strncmp(str, "auto", 4) == 0) {
+ pr_info("Hash pointers mode set to auto.\n");
+ hash_pointers_mode = HASH_PTR_AUTO;
+ } else if (strncmp(str, "never", 5) == 0) {
+ pr_info("Hash pointers mode set to never.\n");
+ hash_pointers_mode = HASH_PTR_NEVER;
+ } else if (strncmp(str, "always", 6) == 0) {
+ pr_info("Hash pointers mode set to always.\n");
+ hash_pointers_mode = HASH_PTR_ALWAYS;
+ } else {
+ pr_warn("Unknown hash_pointers mode '%s' specified; assuming auto.\n", str);
+ hash_pointers_mode = HASH_PTR_AUTO;
+ }
+
+ return 0;
+}
+early_param("hash_pointers", hash_pointers_mode_parse);
+
+static int __init no_hash_pointers_enable(char *str)
+{
+ return hash_pointers_mode_parse("never");
+}
+early_param("no_hash_pointers", no_hash_pointers_enable);
+
/*
* Show a '%p' thing. A kernel extension is that the '%p' is followed
* by an extra set of alphanumeric characters that are extended format
@@ -2105,11 +2397,14 @@ char *fwnode_string(char *buf, char *end, struct fwnode_handle *fwnode,
* - 'S' For symbolic direct pointers (or function descriptors) with offset
* - 's' For symbolic direct pointers (or function descriptors) without offset
* - '[Ss]R' as above with __builtin_extract_return_addr() translation
+ * - 'S[R]b' as above with module build ID (for use in backtraces)
* - '[Ff]' %pf and %pF were obsoleted and later removed in favor of
* %ps and %pS. Be careful when re-using these specifiers.
* - 'B' For backtraced symbolic direct pointers with offset
+ * - 'Bb' as above with module build ID (for use in backtraces)
* - 'R' For decoded struct resource, e.g., [mem 0x0-0x1f 64bit pref]
* - 'r' For raw struct resource, e.g., [mem 0x0-0x1f flags 0x201]
+ * - 'ra' For struct ranges, e.g., [range 0x0000000000000000 - 0x00000000000000ff]
* - 'b[l]' For a bitmap, the number of bits is determined by the field
* width which must be explicitly specified either as part of the
* format string '%32b[l]' or through '%*b[l]', [l] selects
@@ -2134,7 +2429,7 @@ char *fwnode_string(char *buf, char *end, struct fwnode_handle *fwnode,
* [4] or [6] and is able to print port [p], flowinfo [f], scope [s]
* - '[Ii][4S][hnbl]' IPv4 addresses in host, network, big or little endian order
* - 'I[6S]c' for IPv6 addresses printed as specified by
- * http://tools.ietf.org/html/rfc5952
+ * https://tools.ietf.org/html/rfc5952
* - 'E[achnops]' For an escaped buffer, where rules are defined by combination
* of the following flags (see string_escape_mem() for the
* details):
@@ -2162,8 +2457,17 @@ char *fwnode_string(char *buf, char *end, struct fwnode_handle *fwnode,
* Implements a "recursive vsnprintf".
* 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
+ * - 'K' For a kernel pointer that should be hidden from unprivileged users.
+ * Use only for procfs, sysfs and similar files, not printk(); please
+ * read the documentation (path below) first.
* - 'NF' For a netdev_features_t
+ * - '4cc' V4L2 or DRM FourCC code, with endianness and raw numerical value.
+ * - '4c[h[R]lb]' For generic FourCC code with raw numerical value. Both are
+ * displayed in the big-endian format. This is the opposite of V4L2 or
+ * DRM FourCCs.
+ * The additional specifiers define what endianness is used to load
+ * the stored bytes. The data might be interpreted using the host,
+ * reversed host byte order, little-endian, or big-endian.
* - 'h[CDN]' For a variable-length buffer, it prints it as a hex string with
* a certain separator (' ' by default):
* C colon
@@ -2176,13 +2480,13 @@ char *fwnode_string(char *buf, char *end, struct fwnode_handle *fwnode,
* - 'd[234]' For a dentry name (optionally 2-4 last components)
* - 'D[234]' Same as 'd' but for a struct file
* - 'g' For block_device name (gendisk + partition number)
- * - 't[RT][dt][r]' For time and date as represented by:
+ * - 't[RST][dt][r][s]' For time and date as represented by:
* R struct rtc_time
+ * S struct timespec64
* T time64_t
+ * - 'tSp' For time represented by struct timespec64 printed as <seconds>.<nanoseconds>
* - 'C' For a clock, it prints the name (Common Clock Framework) or address
* (legacy clock framework) of the clock
- * - 'Cn' For a clock, it prints the name (Common Clock Framework) or address
- * (legacy clock framework) of the clock
* - 'G' For flags to be printed as a collection of symbolic strings that would
* construct the specific value. Supported flags given by option:
* p page flags (see struct page) given as pointer to unsigned long
@@ -2201,7 +2505,8 @@ char *fwnode_string(char *buf, char *end, struct fwnode_handle *fwnode,
* Without an option prints the full name of the node
* f full name
* P node name, including a possible unit address
- * - 'x' For printing the address. Equivalent to "%lx".
+ * - 'x' For printing the address unmodified. Equivalent to "%lx".
+ * Please read the documentation (path below) before using!
* - '[ku]s' For a BPF/tracing related format specifier, e.g. used out of
* bpf_trace_printk() where [ku] prefix specifies either kernel (k)
* or user (u) memory to probe, and:
@@ -2212,6 +2517,10 @@ char *fwnode_string(char *buf, char *end, struct fwnode_handle *fwnode,
*
* Note: The default behaviour (unadorned %p) is to hash the address,
* rendering it useful as a unique identifier.
+ *
+ * There is also a '%pA' format specifier, but it is only intended to be used
+ * from Rust code to format core::fmt::Arguments. Do *not* use it from C.
+ * See rust/kernel/print.rs for details.
*/
static noinline_for_stack
char *pointer(const char *fmt, char *buf, char *end, void *ptr,
@@ -2221,12 +2530,12 @@ char *pointer(const char *fmt, char *buf, char *end, void *ptr,
case 'S':
case 's':
ptr = dereference_symbol_descriptor(ptr);
- /* Fallthrough */
+ fallthrough;
case 'B':
return symbol_string(buf, end, ptr, spec, fmt);
case 'R':
case 'r':
- return resource_string(buf, end, ptr, spec, fmt);
+ return resource_or_range(fmt, buf, end, ptr, spec);
case 'h':
return hex_string(buf, end, ptr, spec, fmt);
case 'b':
@@ -2256,11 +2565,13 @@ char *pointer(const char *fmt, char *buf, char *end, void *ptr,
case 'U':
return uuid_string(buf, end, ptr, spec, fmt);
case 'V':
- return va_format(buf, end, ptr, spec, fmt);
+ return va_format(buf, end, ptr, spec);
case 'K':
return restricted_pointer(buf, end, ptr, spec);
case 'N':
return netdev_bits(buf, end, ptr, spec, fmt);
+ case '4':
+ return fourcc_string(buf, end, ptr, spec, fmt);
case 'a':
return address_val(buf, end, ptr, spec, fmt);
case 'd':
@@ -2282,12 +2593,18 @@ char *pointer(const char *fmt, char *buf, char *end, void *ptr,
return device_node_string(buf, end, ptr, spec, fmt + 1);
case 'f':
return fwnode_string(buf, end, ptr, spec, fmt + 1);
+ case 'A':
+ if (!IS_ENABLED(CONFIG_RUST)) {
+ WARN_ONCE(1, "Please remove %%pA from non-Rust code\n");
+ return error_string(buf, end, "(%pA?)", spec);
+ }
+ return rust_fmt_argument(buf, end, ptr);
case 'x':
return pointer_string(buf, end, ptr, spec);
case 'e':
/* %pe with a non-ERR_PTR gets treated as plain %p */
if (!IS_ERR(ptr))
- break;
+ return default_pointer(buf, end, ptr, spec);
return err_ptr(buf, end, ptr, spec);
case 'u':
case 'k':
@@ -2297,10 +2614,29 @@ char *pointer(const char *fmt, char *buf, char *end, void *ptr,
default:
return error_string(buf, end, "(einval)", spec);
}
+ default:
+ return default_pointer(buf, end, ptr, spec);
}
+}
- /* default is to _not_ leak addresses, hash before printing */
- return ptr_to_id(buf, end, ptr, spec);
+struct fmt {
+ const char *str;
+ unsigned char state; // enum format_state
+ unsigned char size; // size of numbers
+};
+
+#define SPEC_CHAR(x, flag) [(x)-32] = flag
+static unsigned char spec_flag(unsigned char c)
+{
+ static const unsigned char spec_flag_array[] = {
+ SPEC_CHAR(' ', SPACE),
+ SPEC_CHAR('#', SPECIAL),
+ SPEC_CHAR('+', PLUS),
+ SPEC_CHAR('-', LEFT),
+ SPEC_CHAR('0', ZEROPAD),
+ };
+ c -= 32;
+ return (c < sizeof(spec_flag_array)) ? spec_flag_array[c] : 0;
}
/*
@@ -2325,180 +2661,141 @@ char *pointer(const char *fmt, char *buf, char *end, void *ptr,
* @qualifier: qualifier of a number (long, size_t, ...)
*/
static noinline_for_stack
-int format_decode(const char *fmt, struct printf_spec *spec)
+struct fmt format_decode(struct fmt fmt, struct printf_spec *spec)
{
- const char *start = fmt;
- char qualifier;
+ const char *start = fmt.str;
+ char flag;
/* we finished early by reading the field width */
- if (spec->type == FORMAT_TYPE_WIDTH) {
+ if (unlikely(fmt.state == FORMAT_STATE_WIDTH)) {
if (spec->field_width < 0) {
spec->field_width = -spec->field_width;
spec->flags |= LEFT;
}
- spec->type = FORMAT_TYPE_NONE;
+ fmt.state = FORMAT_STATE_NONE;
goto precision;
}
/* we finished early by reading the precision */
- if (spec->type == FORMAT_TYPE_PRECISION) {
+ if (unlikely(fmt.state == FORMAT_STATE_PRECISION)) {
if (spec->precision < 0)
spec->precision = 0;
- spec->type = FORMAT_TYPE_NONE;
+ fmt.state = FORMAT_STATE_NONE;
goto qualifier;
}
/* By default */
- spec->type = FORMAT_TYPE_NONE;
+ fmt.state = FORMAT_STATE_NONE;
- for (; *fmt ; ++fmt) {
- if (*fmt == '%')
+ for (; *fmt.str ; fmt.str++) {
+ if (*fmt.str == '%')
break;
}
/* Return the current non-format string */
- if (fmt != start || !*fmt)
- return fmt - start;
+ if (fmt.str != start || !*fmt.str)
+ return fmt;
- /* Process flags */
+ /* Process flags. This also skips the first '%' */
spec->flags = 0;
-
- while (1) { /* this also skips first '%' */
- bool found = true;
-
- ++fmt;
-
- switch (*fmt) {
- case '-': spec->flags |= LEFT; break;
- case '+': spec->flags |= PLUS; break;
- case ' ': spec->flags |= SPACE; break;
- case '#': spec->flags |= SPECIAL; break;
- case '0': spec->flags |= ZEROPAD; break;
- default: found = false;
- }
-
- if (!found)
- break;
- }
+ do {
+ /* this also skips first '%' */
+ flag = spec_flag(*++fmt.str);
+ spec->flags |= flag;
+ } while (flag);
/* get field width */
spec->field_width = -1;
- if (isdigit(*fmt))
- spec->field_width = skip_atoi(&fmt);
- else if (*fmt == '*') {
+ if (isdigit(*fmt.str))
+ spec->field_width = skip_atoi(&fmt.str);
+ else if (unlikely(*fmt.str == '*')) {
/* it's the next argument */
- spec->type = FORMAT_TYPE_WIDTH;
- return ++fmt - start;
+ fmt.state = FORMAT_STATE_WIDTH;
+ fmt.str++;
+ return fmt;
}
precision:
/* get the precision */
spec->precision = -1;
- if (*fmt == '.') {
- ++fmt;
- if (isdigit(*fmt)) {
- spec->precision = skip_atoi(&fmt);
+ if (unlikely(*fmt.str == '.')) {
+ fmt.str++;
+ if (isdigit(*fmt.str)) {
+ spec->precision = skip_atoi(&fmt.str);
if (spec->precision < 0)
spec->precision = 0;
- } else if (*fmt == '*') {
+ } else if (*fmt.str == '*') {
/* it's the next argument */
- spec->type = FORMAT_TYPE_PRECISION;
- return ++fmt - start;
+ fmt.state = FORMAT_STATE_PRECISION;
+ fmt.str++;
+ return fmt;
}
}
qualifier:
- /* get the conversion qualifier */
- qualifier = 0;
- if (*fmt == 'h' || _tolower(*fmt) == 'l' ||
- *fmt == 'z' || *fmt == 't') {
- qualifier = *fmt++;
- if (unlikely(qualifier == *fmt)) {
- if (qualifier == 'l') {
- qualifier = 'L';
- ++fmt;
- } else if (qualifier == 'h') {
- qualifier = 'H';
- ++fmt;
- }
- }
- }
-
- /* default base */
+ /* Set up default numeric format */
spec->base = 10;
- switch (*fmt) {
- case 'c':
- spec->type = FORMAT_TYPE_CHAR;
- return ++fmt - start;
+ fmt.state = FORMAT_STATE_NUM;
+ fmt.size = sizeof(int);
+ static const struct format_state {
+ unsigned char state;
+ unsigned char size;
+ unsigned char flags_or_double_size;
+ unsigned char base;
+ } lookup_state[256] = {
+ // Length
+ ['l'] = { 0, sizeof(long), sizeof(long long) },
+ ['L'] = { 0, sizeof(long long) },
+ ['h'] = { 0, sizeof(short), sizeof(char) },
+ ['H'] = { 0, sizeof(char) }, // Questionable historical
+ ['z'] = { 0, sizeof(size_t) },
+ ['t'] = { 0, sizeof(ptrdiff_t) },
+
+ // Non-numeric formats
+ ['c'] = { FORMAT_STATE_CHAR },
+ ['s'] = { FORMAT_STATE_STR },
+ ['p'] = { FORMAT_STATE_PTR },
+ ['%'] = { FORMAT_STATE_PERCENT_CHAR },
+
+ // Numerics
+ ['o'] = { FORMAT_STATE_NUM, 0, 0, 8 },
+ ['x'] = { FORMAT_STATE_NUM, 0, SMALL, 16 },
+ ['X'] = { FORMAT_STATE_NUM, 0, 0, 16 },
+ ['d'] = { FORMAT_STATE_NUM, 0, SIGN, 10 },
+ ['i'] = { FORMAT_STATE_NUM, 0, SIGN, 10 },
+ ['u'] = { FORMAT_STATE_NUM, 0, 0, 10, },
- case 's':
- spec->type = FORMAT_TYPE_STR;
- return ++fmt - start;
-
- case 'p':
- spec->type = FORMAT_TYPE_PTR;
- return ++fmt - start;
-
- case '%':
- spec->type = FORMAT_TYPE_PERCENT_CHAR;
- return ++fmt - start;
-
- /* integer number formats - set up the flags and "break" */
- case 'o':
- spec->base = 8;
- break;
-
- case 'x':
- spec->flags |= SMALL;
- /* fall through */
-
- case 'X':
- spec->base = 16;
- break;
-
- case 'd':
- case 'i':
- spec->flags |= SIGN;
- case 'u':
- break;
-
- case 'n':
/*
* Since %n poses a greater security risk than
* utility, treat it as any other invalid or
* unsupported format specifier.
*/
- /* Fall-through */
+ };
- default:
- WARN_ONCE(1, "Please remove unsupported %%%c in format string\n", *fmt);
- spec->type = FORMAT_TYPE_INVALID;
- return fmt - start;
- }
-
- if (qualifier == 'L')
- spec->type = FORMAT_TYPE_LONG_LONG;
- else if (qualifier == 'l') {
- BUILD_BUG_ON(FORMAT_TYPE_ULONG + SIGN != FORMAT_TYPE_LONG);
- spec->type = FORMAT_TYPE_ULONG + (spec->flags & SIGN);
- } else if (qualifier == 'z') {
- spec->type = FORMAT_TYPE_SIZE_T;
- } else if (qualifier == 't') {
- spec->type = FORMAT_TYPE_PTRDIFF;
- } else if (qualifier == 'H') {
- BUILD_BUG_ON(FORMAT_TYPE_UBYTE + SIGN != FORMAT_TYPE_BYTE);
- spec->type = FORMAT_TYPE_UBYTE + (spec->flags & SIGN);
- } else if (qualifier == 'h') {
- BUILD_BUG_ON(FORMAT_TYPE_USHORT + SIGN != FORMAT_TYPE_SHORT);
- spec->type = FORMAT_TYPE_USHORT + (spec->flags & SIGN);
- } else {
- BUILD_BUG_ON(FORMAT_TYPE_UINT + SIGN != FORMAT_TYPE_INT);
- spec->type = FORMAT_TYPE_UINT + (spec->flags & SIGN);
+ const struct format_state *p = lookup_state + (u8)*fmt.str;
+ if (p->size) {
+ fmt.size = p->size;
+ if (p->flags_or_double_size && fmt.str[0] == fmt.str[1]) {
+ fmt.size = p->flags_or_double_size;
+ fmt.str++;
+ }
+ fmt.str++;
+ p = lookup_state + *fmt.str;
+ }
+ if (p->state) {
+ if (p->base)
+ spec->base = p->base;
+ spec->flags |= p->flags_or_double_size;
+ fmt.state = p->state;
+ fmt.str++;
+ return fmt;
}
- return ++fmt - start;
+ WARN_ONCE(1, "Please remove unsupported %%%c in format string\n", *fmt.str);
+ fmt.state = FORMAT_STATE_INVALID;
+ return fmt;
}
static void
@@ -2519,11 +2816,27 @@ set_precision(struct printf_spec *spec, int prec)
}
}
+/*
+ * Turn a 1/2/4-byte value into a 64-bit one for printing: truncate
+ * as necessary and deal with signedness.
+ *
+ * 'size' is the size of the value in bytes.
+ */
+static unsigned long long convert_num_spec(unsigned int val, int size, struct printf_spec spec)
+{
+ unsigned int shift = 32 - size*8;
+
+ val <<= shift;
+ if (!(spec.flags & SIGN))
+ return val >> shift;
+ return (int)val >> shift;
+}
+
/**
* vsnprintf - Format a string and place it in a buffer
* @buf: The buffer to place the result into
* @size: The size of the buffer, including the trailing null space
- * @fmt: The format string to use
+ * @fmt_str: The format string to use
* @args: Arguments for the format string
*
* This function generally follows C99 vsnprintf, but has some
@@ -2547,11 +2860,14 @@ set_precision(struct printf_spec *spec, int prec)
*
* If you're not already dealing with a va_list consider using snprintf().
*/
-int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
+int vsnprintf(char *buf, size_t size, const char *fmt_str, va_list args)
{
- unsigned long long num;
char *str, *end;
struct printf_spec spec = {0};
+ struct fmt fmt = {
+ .str = fmt_str,
+ .state = FORMAT_STATE_NONE,
+ };
/* Reject out-of-range values early. Large positive sizes are
used for unknown buffer sizes. */
@@ -2567,33 +2883,44 @@ int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
size = end - buf;
}
- while (*fmt) {
- const char *old_fmt = fmt;
- int read = format_decode(fmt, &spec);
+ while (*fmt.str) {
+ const char *old_fmt = fmt.str;
- fmt += read;
+ fmt = format_decode(fmt, &spec);
- switch (spec.type) {
- case FORMAT_TYPE_NONE: {
- int copy = read;
+ switch (fmt.state) {
+ case FORMAT_STATE_NONE: {
+ int read = fmt.str - old_fmt;
if (str < end) {
+ int copy = read;
if (copy > end - str)
copy = end - str;
memcpy(str, old_fmt, copy);
}
str += read;
- break;
+ continue;
}
- case FORMAT_TYPE_WIDTH:
+ case FORMAT_STATE_NUM: {
+ unsigned long long num;
+
+ if (fmt.size > sizeof(int))
+ num = va_arg(args, long long);
+ else
+ num = convert_num_spec(va_arg(args, int), fmt.size, spec);
+ str = number(str, end, num, spec);
+ continue;
+ }
+
+ case FORMAT_STATE_WIDTH:
set_field_width(&spec, va_arg(args, int));
- break;
+ continue;
- case FORMAT_TYPE_PRECISION:
+ case FORMAT_STATE_PRECISION:
set_precision(&spec, va_arg(args, int));
- break;
+ continue;
- case FORMAT_TYPE_CHAR: {
+ case FORMAT_STATE_CHAR: {
char c;
if (!(spec.flags & LEFT)) {
@@ -2613,27 +2940,27 @@ int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
*str = ' ';
++str;
}
- break;
+ continue;
}
- case FORMAT_TYPE_STR:
+ case FORMAT_STATE_STR:
str = string(str, end, va_arg(args, char *), spec);
- break;
+ continue;
- case FORMAT_TYPE_PTR:
- str = pointer(fmt, str, end, va_arg(args, void *),
+ case FORMAT_STATE_PTR:
+ str = pointer(fmt.str, str, end, va_arg(args, void *),
spec);
- while (isalnum(*fmt))
- fmt++;
- break;
+ while (isalnum(*fmt.str))
+ fmt.str++;
+ continue;
- case FORMAT_TYPE_PERCENT_CHAR:
+ case FORMAT_STATE_PERCENT_CHAR:
if (str < end)
*str = '%';
++str;
- break;
+ continue;
- case FORMAT_TYPE_INVALID:
+ default:
/*
* Presumably the arguments passed gcc's type
* checking, but there is no safe or sane way
@@ -2643,47 +2970,6 @@ int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
* sync.
*/
goto out;
-
- default:
- switch (spec.type) {
- case FORMAT_TYPE_LONG_LONG:
- num = va_arg(args, long long);
- break;
- case FORMAT_TYPE_ULONG:
- num = va_arg(args, unsigned long);
- break;
- case FORMAT_TYPE_LONG:
- num = va_arg(args, long);
- break;
- case FORMAT_TYPE_SIZE_T:
- if (spec.flags & SIGN)
- num = va_arg(args, ssize_t);
- else
- num = va_arg(args, size_t);
- break;
- case FORMAT_TYPE_PTRDIFF:
- num = va_arg(args, ptrdiff_t);
- break;
- case FORMAT_TYPE_UBYTE:
- num = (unsigned char) va_arg(args, int);
- break;
- case FORMAT_TYPE_BYTE:
- num = (signed char) va_arg(args, int);
- break;
- case FORMAT_TYPE_USHORT:
- num = (unsigned short) va_arg(args, int);
- break;
- case FORMAT_TYPE_SHORT:
- num = (short) va_arg(args, int);
- break;
- case FORMAT_TYPE_INT:
- num = (int) va_arg(args, int);
- break;
- default:
- num = va_arg(args, unsigned int);
- }
-
- str = number(str, end, num, spec);
}
}
@@ -2720,13 +3006,15 @@ int vscnprintf(char *buf, size_t size, const char *fmt, va_list args)
{
int i;
+ if (unlikely(!size))
+ return 0;
+
i = vsnprintf(buf, size, fmt, args);
if (likely(i < size))
return i;
- if (size != 0)
- return size - 1;
- return 0;
+
+ return size - 1;
}
EXPORT_SYMBOL(vscnprintf);
@@ -2787,8 +3075,8 @@ EXPORT_SYMBOL(scnprintf);
* @fmt: The format string to use
* @args: Arguments for the format string
*
- * The function returns the number of characters written
- * into @buf. Use vsnprintf() or vscnprintf() in order to avoid
+ * The return value is the number of characters written into @buf not including
+ * the trailing '\0'. Use vsnprintf() or vscnprintf() in order to avoid
* buffer overflows.
*
* If you're not already dealing with a va_list consider using sprintf().
@@ -2807,8 +3095,8 @@ EXPORT_SYMBOL(vsprintf);
* @fmt: The format string to use
* @...: Arguments for the format string
*
- * The function returns the number of characters written
- * into @buf. Use snprintf() or scnprintf() in order to avoid
+ * The return value is the number of characters written into @buf not including
+ * the trailing '\0'. Use snprintf() or scnprintf() in order to avoid
* buffer overflows.
*
* See the vsnprintf() documentation for format string extensions over C99.
@@ -2837,7 +3125,7 @@ EXPORT_SYMBOL(sprintf);
* vbin_printf - Parse a format string and place args' binary value in a buffer
* @bin_buf: The buffer to place args' binary value
* @size: The size of the buffer(by words(32bits), not characters)
- * @fmt: The format string to use
+ * @fmt_str: The format string to use
* @args: Arguments for the format string
*
* The format follows C99 vsnprintf, except %n is ignored, and its argument
@@ -2850,8 +3138,12 @@ EXPORT_SYMBOL(sprintf);
* If the return value is greater than @size, the resulting bin_buf is NOT
* valid for bstr_printf().
*/
-int vbin_printf(u32 *bin_buf, size_t size, const char *fmt, va_list args)
+int vbin_printf(u32 *bin_buf, size_t size, const char *fmt_str, va_list args)
{
+ struct fmt fmt = {
+ .str = fmt_str,
+ .state = FORMAT_STATE_NONE,
+ };
struct printf_spec spec = {0};
char *str, *end;
int width;
@@ -2883,31 +3175,29 @@ int vbin_printf(u32 *bin_buf, size_t size, const char *fmt, va_list args)
value; \
})
- while (*fmt) {
- int read = format_decode(fmt, &spec);
-
- fmt += read;
+ while (*fmt.str) {
+ fmt = format_decode(fmt, &spec);
- switch (spec.type) {
- case FORMAT_TYPE_NONE:
- case FORMAT_TYPE_PERCENT_CHAR:
+ switch (fmt.state) {
+ case FORMAT_STATE_NONE:
+ case FORMAT_STATE_PERCENT_CHAR:
break;
- case FORMAT_TYPE_INVALID:
+ case FORMAT_STATE_INVALID:
goto out;
- case FORMAT_TYPE_WIDTH:
- case FORMAT_TYPE_PRECISION:
+ case FORMAT_STATE_WIDTH:
+ case FORMAT_STATE_PRECISION:
width = (int)save_arg(int);
/* Pointers may require the width */
- if (*fmt == 'p')
+ if (*fmt.str == 'p')
set_field_width(&spec, width);
break;
- case FORMAT_TYPE_CHAR:
+ case FORMAT_STATE_CHAR:
save_arg(char);
break;
- case FORMAT_TYPE_STR: {
+ case FORMAT_STATE_STR: {
const char *save_str = va_arg(args, char *);
const char *err_msg;
size_t len;
@@ -2923,9 +3213,9 @@ int vbin_printf(u32 *bin_buf, size_t size, const char *fmt, va_list args)
break;
}
- case FORMAT_TYPE_PTR:
+ case FORMAT_STATE_PTR:
/* Dereferenced pointers must be done now */
- switch (*fmt) {
+ switch (*fmt.str) {
/* Dereference of functions is still OK */
case 'S':
case 's':
@@ -2935,11 +3225,11 @@ int vbin_printf(u32 *bin_buf, size_t size, const char *fmt, va_list args)
save_arg(void *);
break;
default:
- if (!isalnum(*fmt)) {
+ if (!isalnum(*fmt.str)) {
save_arg(void *);
break;
}
- str = pointer(fmt, str, end, va_arg(args, void *),
+ str = pointer(fmt.str, str, end, va_arg(args, void *),
spec);
if (str + 1 < end)
*str++ = '\0';
@@ -2947,35 +3237,14 @@ int vbin_printf(u32 *bin_buf, size_t size, const char *fmt, va_list args)
end[-1] = '\0'; /* Must be nul terminated */
}
/* skip all alphanumeric pointer suffixes */
- while (isalnum(*fmt))
- fmt++;
+ while (isalnum(*fmt.str))
+ fmt.str++;
break;
- default:
- switch (spec.type) {
-
- case FORMAT_TYPE_LONG_LONG:
+ case FORMAT_STATE_NUM:
+ if (fmt.size > sizeof(int)) {
save_arg(long long);
- break;
- case FORMAT_TYPE_ULONG:
- case FORMAT_TYPE_LONG:
- save_arg(unsigned long);
- break;
- case FORMAT_TYPE_SIZE_T:
- save_arg(size_t);
- break;
- case FORMAT_TYPE_PTRDIFF:
- save_arg(ptrdiff_t);
- break;
- case FORMAT_TYPE_UBYTE:
- case FORMAT_TYPE_BYTE:
- save_arg(char);
- break;
- case FORMAT_TYPE_USHORT:
- case FORMAT_TYPE_SHORT:
- save_arg(short);
- break;
- default:
+ } else {
save_arg(int);
}
}
@@ -2991,7 +3260,7 @@ EXPORT_SYMBOL_GPL(vbin_printf);
* bstr_printf - Format a string from binary arguments and place it in a buffer
* @buf: The buffer to place the result into
* @size: The size of the buffer, including the trailing null space
- * @fmt: The format string to use
+ * @fmt_str: The format string to use
* @bin_buf: Binary arguments for the format string
*
* This function like C99 vsnprintf, but the difference is that vsnprintf gets
@@ -3009,8 +3278,12 @@ EXPORT_SYMBOL_GPL(vbin_printf);
* return is greater than or equal to @size, the resulting
* string is truncated.
*/
-int bstr_printf(char *buf, size_t size, const char *fmt, const u32 *bin_buf)
+int bstr_printf(char *buf, size_t size, const char *fmt_str, const u32 *bin_buf)
{
+ struct fmt fmt = {
+ .str = fmt_str,
+ .state = FORMAT_STATE_NONE,
+ };
struct printf_spec spec = {0};
char *str, *end;
const char *args = (const char *)bin_buf;
@@ -3042,33 +3315,33 @@ int bstr_printf(char *buf, size_t size, const char *fmt, const u32 *bin_buf)
size = end - buf;
}
- while (*fmt) {
- const char *old_fmt = fmt;
- int read = format_decode(fmt, &spec);
-
- fmt += read;
+ while (*fmt.str) {
+ const char *old_fmt = fmt.str;
+ unsigned long long num;
- switch (spec.type) {
- case FORMAT_TYPE_NONE: {
- int copy = read;
+ fmt = format_decode(fmt, &spec);
+ switch (fmt.state) {
+ case FORMAT_STATE_NONE: {
+ int read = fmt.str - old_fmt;
if (str < end) {
+ int copy = read;
if (copy > end - str)
copy = end - str;
memcpy(str, old_fmt, copy);
}
str += read;
- break;
+ continue;
}
- case FORMAT_TYPE_WIDTH:
+ case FORMAT_STATE_WIDTH:
set_field_width(&spec, get_arg(int));
- break;
+ continue;
- case FORMAT_TYPE_PRECISION:
+ case FORMAT_STATE_PRECISION:
set_precision(&spec, get_arg(int));
- break;
+ continue;
- case FORMAT_TYPE_CHAR: {
+ case FORMAT_STATE_CHAR: {
char c;
if (!(spec.flags & LEFT)) {
@@ -3087,32 +3360,30 @@ int bstr_printf(char *buf, size_t size, const char *fmt, const u32 *bin_buf)
*str = ' ';
++str;
}
- break;
+ continue;
}
- case FORMAT_TYPE_STR: {
+ case FORMAT_STATE_STR: {
const char *str_arg = args;
args += strlen(str_arg) + 1;
str = string(str, end, (char *)str_arg, spec);
- break;
+ continue;
}
- case FORMAT_TYPE_PTR: {
+ case FORMAT_STATE_PTR: {
bool process = false;
int copy, len;
/* Non function dereferences were already done */
- switch (*fmt) {
+ switch (*fmt.str) {
case 'S':
case 's':
- case 'F':
- case 'f':
case 'x':
case 'K':
case 'e':
process = true;
break;
default:
- if (!isalnum(*fmt)) {
+ if (!isalnum(*fmt.str)) {
process = true;
break;
}
@@ -3127,63 +3398,31 @@ int bstr_printf(char *buf, size_t size, const char *fmt, const u32 *bin_buf)
}
}
if (process)
- str = pointer(fmt, str, end, get_arg(void *), spec);
+ str = pointer(fmt.str, str, end, get_arg(void *), spec);
- while (isalnum(*fmt))
- fmt++;
- break;
+ while (isalnum(*fmt.str))
+ fmt.str++;
+ continue;
}
- case FORMAT_TYPE_PERCENT_CHAR:
+ case FORMAT_STATE_PERCENT_CHAR:
if (str < end)
*str = '%';
++str;
- break;
+ continue;
- case FORMAT_TYPE_INVALID:
+ case FORMAT_STATE_INVALID:
goto out;
- default: {
- unsigned long long num;
-
- switch (spec.type) {
-
- case FORMAT_TYPE_LONG_LONG:
+ case FORMAT_STATE_NUM:
+ if (fmt.size > sizeof(int))
num = get_arg(long long);
- break;
- case FORMAT_TYPE_ULONG:
- case FORMAT_TYPE_LONG:
- num = get_arg(unsigned long);
- break;
- case FORMAT_TYPE_SIZE_T:
- num = get_arg(size_t);
- break;
- case FORMAT_TYPE_PTRDIFF:
- num = get_arg(ptrdiff_t);
- break;
- case FORMAT_TYPE_UBYTE:
- num = get_arg(unsigned char);
- break;
- case FORMAT_TYPE_BYTE:
- num = get_arg(signed char);
- break;
- case FORMAT_TYPE_USHORT:
- num = get_arg(unsigned short);
- break;
- case FORMAT_TYPE_SHORT:
- num = get_arg(short);
- break;
- case FORMAT_TYPE_UINT:
- num = get_arg(unsigned int);
- break;
- default:
- num = get_arg(int);
- }
-
+ else
+ num = convert_num_spec(get_arg(int), fmt.size, spec);
str = number(str, end, num, spec);
- } /* default: */
- } /* switch(spec.type) */
- } /* while(*fmt) */
+ continue;
+ }
+ } /* while(*fmt.str) */
out:
if (size > 0) {
@@ -3200,29 +3439,6 @@ out:
}
EXPORT_SYMBOL_GPL(bstr_printf);
-/**
- * bprintf - Parse a format string and place args' binary value in a buffer
- * @bin_buf: The buffer to place args' binary value
- * @size: The size of the buffer(by words(32bits), not characters)
- * @fmt: The format string to use
- * @...: Arguments for the format string
- *
- * The function returns the number of words(u32) written
- * into @bin_buf.
- */
-int bprintf(u32 *bin_buf, size_t size, const char *fmt, ...)
-{
- va_list args;
- int ret;
-
- va_start(args, fmt);
- ret = vbin_printf(bin_buf, size, fmt, args);
- va_end(args);
-
- return ret;
-}
-EXPORT_SYMBOL_GPL(bprintf);
-
#endif /* CONFIG_BINARY_PRINTF */
/**
@@ -3248,7 +3464,7 @@ int vsscanf(const char *buf, const char *fmt, va_list args)
while (*fmt) {
/* skip any white space in format */
- /* white space in format matchs any amount of
+ /* white space in format matches any amount of
* white space, including none, in the input.
*/
if (isspace(*fmt)) {
@@ -3381,7 +3597,7 @@ int vsscanf(const char *buf, const char *fmt, va_list args)
++fmt;
for ( ; *fmt && *fmt != ']'; ++fmt, ++len)
- set_bit((u8)*fmt, set);
+ __set_bit((u8)*fmt, set);
/* no ']' or no character set found */
if (!*fmt || !len)
@@ -3391,7 +3607,7 @@ int vsscanf(const char *buf, const char *fmt, va_list args)
if (negate) {
bitmap_complement(set, set, 256);
/* exclude null '\0' byte */
- clear_bit(0, set);
+ __clear_bit(0, set);
}
/* match must be non-empty */
@@ -3413,10 +3629,10 @@ int vsscanf(const char *buf, const char *fmt, va_list args)
break;
case 'i':
base = 0;
- /* fall through */
+ fallthrough;
case 'd':
is_sign = true;
- /* fall through */
+ fallthrough;
case 'u':
break;
case '%':
@@ -3435,36 +3651,26 @@ int vsscanf(const char *buf, const char *fmt, va_list args)
str = skip_spaces(str);
digit = *str;
- if (is_sign && digit == '-')
+ if (is_sign && digit == '-') {
+ if (field_width == 1)
+ break;
+
digit = *(str + 1);
+ }
if (!digit
|| (base == 16 && !isxdigit(digit))
|| (base == 10 && !isdigit(digit))
- || (base == 8 && (!isdigit(digit) || digit > '7'))
+ || (base == 8 && !isodigit(digit))
|| (base == 0 && !isdigit(digit)))
break;
if (is_sign)
- val.s = qualifier != 'L' ?
- simple_strtol(str, &next, base) :
- simple_strtoll(str, &next, base);
+ val.s = simple_strntoll(str, &next, base,
+ field_width >= 0 ? field_width : INT_MAX);
else
- val.u = qualifier != 'L' ?
- simple_strtoul(str, &next, base) :
- simple_strtoull(str, &next, base);
-
- if (field_width > 0 && next - str > field_width) {
- if (base == 0)
- _parse_integer_fixup_radix(str, &base);
- while (next - str > field_width) {
- if (is_sign)
- val.s = div_s64(val.s, base);
- else
- val.u = div_u64(val.u, base);
- --next;
- }
- }
+ val.u = simple_strntoull(str, &next, base,
+ field_width >= 0 ? field_width : INT_MAX);
switch (qualifier) {
case 'H': /* that's 'hh' in format */