diff options
Diffstat (limited to 'lib/vsprintf.c')
| -rw-r--r-- | lib/vsprintf.c | 132 | 
1 files changed, 79 insertions, 53 deletions
diff --git a/lib/vsprintf.c b/lib/vsprintf.c index 26559bdb4c4..6fe2c84eb05 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -27,6 +27,7 @@  #include <linux/uaccess.h>  #include <linux/ioport.h>  #include <linux/dcache.h> +#include <linux/cred.h>  #include <net/addrconf.h>  #include <asm/page.h>		/* for PAGE_SIZE */ @@ -363,7 +364,6 @@ enum format_type {  	FORMAT_TYPE_SHORT,  	FORMAT_TYPE_UINT,  	FORMAT_TYPE_INT, -	FORMAT_TYPE_NRCHARS,  	FORMAT_TYPE_SIZE_T,  	FORMAT_TYPE_PTRDIFF  }; @@ -718,10 +718,15 @@ char *resource_string(char *buf, char *end, struct resource *res,  		specp = &mem_spec;  		decode = 0;  	} -	p = number(p, pend, res->start, *specp); -	if (res->start != res->end) { -		*p++ = '-'; -		p = number(p, pend, res->end, *specp); +	if (decode && res->flags & IORESOURCE_UNSET) { +		p = string(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); +		}  	}  	if (decode) {  		if (res->flags & IORESOURCE_MEM_64) @@ -1154,6 +1159,30 @@ char *netdev_feature_string(char *buf, char *end, const u8 *addr,  	return number(buf, end, *(const netdev_features_t *)addr, spec);  } +static noinline_for_stack +char *address_val(char *buf, char *end, const void *addr, +		  struct printf_spec spec, const char *fmt) +{ +	unsigned long long num; + +	spec.flags |= SPECIAL | SMALL | ZEROPAD; +	spec.base = 16; + +	switch (fmt[1]) { +	case 'd': +		num = *(const dma_addr_t *)addr; +		spec.field_width = sizeof(dma_addr_t) * 2 + 2; +		break; +	case 'p': +	default: +		num = *(const phys_addr_t *)addr; +		spec.field_width = sizeof(phys_addr_t) * 2 + 2; +		break; +	} + +	return number(buf, end, num, spec); +} +  int kptr_restrict __read_mostly;  /* @@ -1217,7 +1246,10 @@ int kptr_restrict __read_mostly;   *              N no separator   *            The maximum supported length is 64 bytes of the input. Consider   *            to use print_hex_dump() for the larger input. - * - 'a' For a phys_addr_t type and its derivative types (passed by reference) + * - 'a[pd]' For address types [p] phys_addr_t, [d] dma_addr_t and derivatives + *           (default assumed to be phys_addr_t, passed by reference) + * - 'd[234]' For a dentry name (optionally 2-4 last components) + * - 'D[234]' Same as 'd' but for a struct file   *   * Note: The difference between 'S' and 'F' is that on ia64 and ppc64   * function pointers are really function descriptors, which contain a @@ -1312,11 +1344,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': @@ -1324,11 +1382,7 @@ char *pointer(const char *fmt, char *buf, char *end, void *ptr,  		}  		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); +		return address_val(buf, end, ptr, spec, fmt);  	case 'd':  		return dentry_name(buf, end, ptr, spec, fmt);  	case 'D': @@ -1483,10 +1537,6 @@ qualifier:  		return fmt - start;  		/* skip alnum */ -	case 'n': -		spec->type = FORMAT_TYPE_NRCHARS; -		return ++fmt - start; -  	case '%':  		spec->type = FORMAT_TYPE_PERCENT_CHAR;  		return ++fmt - start; @@ -1509,6 +1559,15 @@ qualifier:  	case 'u':  		break; +	case 'n': +		/* +		 * Since %n poses a greater security risk than utility, treat +		 * it as an invalid format specifier. Warn about its use so +		 * that new instances don't get added. +		 */ +		WARN_ONCE(1, "Please remove ignored %%n in '%s'\n", fmt); +		/* Fall-through */ +  	default:  		spec->type = FORMAT_TYPE_INVALID;  		return fmt - start; @@ -1682,22 +1741,6 @@ int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)  			++str;  			break; -		case FORMAT_TYPE_NRCHARS: { -			u8 qualifier = spec.qualifier; - -			if (qualifier == 'l') { -				long *ip = va_arg(args, long *); -				*ip = (str - buf); -			} else if (_tolower(qualifier) == 'z') { -				size_t *ip = va_arg(args, size_t *); -				*ip = (str - buf); -			} else { -				int *ip = va_arg(args, int *); -				*ip = (str - buf); -			} -			break; -		} -  		default:  			switch (spec.type) {  			case FORMAT_TYPE_LONG_LONG: @@ -1972,19 +2015,6 @@ do {									\  				fmt++;  			break; -		case FORMAT_TYPE_NRCHARS: { -			/* skip %n 's argument */ -			u8 qualifier = spec.qualifier; -			void *skip_arg; -			if (qualifier == 'l') -				skip_arg = va_arg(args, long *); -			else if (_tolower(qualifier) == 'z') -				skip_arg = va_arg(args, size_t *); -			else -				skip_arg = va_arg(args, int *); -			break; -		} -  		default:  			switch (spec.type) { @@ -2143,10 +2173,6 @@ int bstr_printf(char *buf, size_t size, const char *fmt, const u32 *bin_buf)  			++str;  			break; -		case FORMAT_TYPE_NRCHARS: -			/* skip */ -			break; -  		default: {  			unsigned long long num; @@ -2321,7 +2347,7 @@ int vsscanf(const char *buf, const char *fmt, va_list args)  			break;  		base = 10; -		is_sign = 0; +		is_sign = false;  		switch (*fmt++) {  		case 'c': @@ -2360,7 +2386,7 @@ int vsscanf(const char *buf, const char *fmt, va_list args)  		case 'i':  			base = 0;  		case 'd': -			is_sign = 1; +			is_sign = true;  		case 'u':  			break;  		case '%':  | 
