linux/drivers/acpi/acpica/utprint.c
John Levon bb89a79ac6 ACPICA: utilities: fix sprintf()
This contains changes for the following ACPICA commit ID's:
8f99a6ccd3b8e5c3d3d68c53fdbb054c2477eeb4
d30647af53abd334cbcf6362387464ea647bac9e
d3c5fb4cf5b2880d789c987eb847fc3de3774abc

On 32-bit, the provided sprintf() is non-functional: with a size of
ACPI_UINT32_MAX, String + Size will wrap, meaning End < Start, and
acpi_ut_bound_string_output() will never output anything as a result.

The symptom we saw of this was acpixtract failing to output anything.

Link: https://github.com/acpica/acpica/commit/8f99a6cc
Link: https://github.com/acpica/acpica/commit/d30647af
Link: https://github.com/acpica/acpica/commit/d3c5fb4c
Signed-off-by: MSathieu <18145111+MSathieu@users.noreply.github.com>
Signed-off-by: John Levon <john.levon@joyent.com>
Signed-off-by: Bob Moore <robert.moore@intel.com>
Signed-off-by: Erik Kaneda <erik.kaneda@intel.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
2020-03-30 14:52:32 +02:00

715 lines
16 KiB
C

// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: utprint - Formatted printing routines
*
* Copyright (C) 2000 - 2020, Intel Corp.
*
*****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
#define _COMPONENT ACPI_UTILITIES
ACPI_MODULE_NAME("utprint")
#define ACPI_FORMAT_SIGN 0x01
#define ACPI_FORMAT_SIGN_PLUS 0x02
#define ACPI_FORMAT_SIGN_PLUS_SPACE 0x04
#define ACPI_FORMAT_ZERO 0x08
#define ACPI_FORMAT_LEFT 0x10
#define ACPI_FORMAT_UPPER 0x20
#define ACPI_FORMAT_PREFIX 0x40
/* Local prototypes */
static acpi_size
acpi_ut_bound_string_length(const char *string, acpi_size count);
static char *acpi_ut_bound_string_output(char *string, const char *end, char c);
static char *acpi_ut_format_number(char *string,
char *end,
u64 number,
u8 base, s32 width, s32 precision, u8 type);
static char *acpi_ut_put_number(char *string, u64 number, u8 base, u8 upper);
/*******************************************************************************
*
* FUNCTION: acpi_ut_bound_string_length
*
* PARAMETERS: string - String with boundary
* count - Boundary of the string
*
* RETURN: Length of the string. Less than or equal to Count.
*
* DESCRIPTION: Calculate the length of a string with boundary.
*
******************************************************************************/
static acpi_size
acpi_ut_bound_string_length(const char *string, acpi_size count)
{
u32 length = 0;
while (*string && count) {
length++;
string++;
count--;
}
return (length);
}
/*******************************************************************************
*
* FUNCTION: acpi_ut_bound_string_output
*
* PARAMETERS: string - String with boundary
* end - Boundary of the string
* c - Character to be output to the string
*
* RETURN: Updated position for next valid character
*
* DESCRIPTION: Output a character into a string with boundary check.
*
******************************************************************************/
static char *acpi_ut_bound_string_output(char *string, const char *end, char c)
{
if (string < end) {
*string = c;
}
++string;
return (string);
}
/*******************************************************************************
*
* FUNCTION: acpi_ut_put_number
*
* PARAMETERS: string - Buffer to hold reverse-ordered string
* number - Integer to be converted
* base - Base of the integer
* upper - Whether or not using upper cased digits
*
* RETURN: Updated position for next valid character
*
* DESCRIPTION: Convert an integer into a string, note that, the string holds a
* reversed ordered number without the trailing zero.
*
******************************************************************************/
static char *acpi_ut_put_number(char *string, u64 number, u8 base, u8 upper)
{
const char *digits;
u64 digit_index;
char *pos;
pos = string;
digits = upper ? acpi_gbl_upper_hex_digits : acpi_gbl_lower_hex_digits;
if (number == 0) {
*(pos++) = '0';
} else {
while (number) {
(void)acpi_ut_divide(number, base, &number,
&digit_index);
*(pos++) = digits[digit_index];
}
}
/* *(Pos++) = '0'; */
return (pos);
}
/*******************************************************************************
*
* FUNCTION: acpi_ut_scan_number
*
* PARAMETERS: string - String buffer
* number_ptr - Where the number is returned
*
* RETURN: Updated position for next valid character
*
* DESCRIPTION: Scan a string for a decimal integer.
*
******************************************************************************/
const char *acpi_ut_scan_number(const char *string, u64 *number_ptr)
{
u64 number = 0;
while (isdigit((int)*string)) {
acpi_ut_short_multiply(number, 10, &number);
number += *(string++) - '0';
}
*number_ptr = number;
return (string);
}
/*******************************************************************************
*
* FUNCTION: acpi_ut_print_number
*
* PARAMETERS: string - String buffer
* number - The number to be converted
*
* RETURN: Updated position for next valid character
*
* DESCRIPTION: Print a decimal integer into a string.
*
******************************************************************************/
const char *acpi_ut_print_number(char *string, u64 number)
{
char ascii_string[20];
const char *pos1;
char *pos2;
pos1 = acpi_ut_put_number(ascii_string, number, 10, FALSE);
pos2 = string;
while (pos1 != ascii_string) {
*(pos2++) = *(--pos1);
}
*pos2 = 0;
return (string);
}
/*******************************************************************************
*
* FUNCTION: acpi_ut_format_number
*
* PARAMETERS: string - String buffer with boundary
* end - Boundary of the string
* number - The number to be converted
* base - Base of the integer
* width - Field width
* precision - Precision of the integer
* type - Special printing flags
*
* RETURN: Updated position for next valid character
*
* DESCRIPTION: Print an integer into a string with any base and any precision.
*
******************************************************************************/
static char *acpi_ut_format_number(char *string,
char *end,
u64 number,
u8 base, s32 width, s32 precision, u8 type)
{
char *pos;
char sign;
char zero;
u8 need_prefix;
u8 upper;
s32 i;
char reversed_string[66];
/* Parameter validation */
if (base < 2 || base > 16) {
return (NULL);
}
if (type & ACPI_FORMAT_LEFT) {
type &= ~ACPI_FORMAT_ZERO;
}
need_prefix = ((type & ACPI_FORMAT_PREFIX)
&& base != 10) ? TRUE : FALSE;
upper = (type & ACPI_FORMAT_UPPER) ? TRUE : FALSE;
zero = (type & ACPI_FORMAT_ZERO) ? '0' : ' ';
/* Calculate size according to sign and prefix */
sign = '\0';
if (type & ACPI_FORMAT_SIGN) {
if ((s64)number < 0) {
sign = '-';
number = -(s64)number;
width--;
} else if (type & ACPI_FORMAT_SIGN_PLUS) {
sign = '+';
width--;
} else if (type & ACPI_FORMAT_SIGN_PLUS_SPACE) {
sign = ' ';
width--;
}
}
if (need_prefix) {
width--;
if (base == 16) {
width--;
}
}
/* Generate full string in reverse order */
pos = acpi_ut_put_number(reversed_string, number, base, upper);
i = (s32)ACPI_PTR_DIFF(pos, reversed_string);
/* Printing 100 using %2d gives "100", not "00" */
if (i > precision) {
precision = i;
}
width -= precision;
/* Output the string */
if (!(type & (ACPI_FORMAT_ZERO | ACPI_FORMAT_LEFT))) {
while (--width >= 0) {
string = acpi_ut_bound_string_output(string, end, ' ');
}
}
if (sign) {
string = acpi_ut_bound_string_output(string, end, sign);
}
if (need_prefix) {
string = acpi_ut_bound_string_output(string, end, '0');
if (base == 16) {
string =
acpi_ut_bound_string_output(string, end,
upper ? 'X' : 'x');
}
}
if (!(type & ACPI_FORMAT_LEFT)) {
while (--width >= 0) {
string = acpi_ut_bound_string_output(string, end, zero);
}
}
while (i <= --precision) {
string = acpi_ut_bound_string_output(string, end, '0');
}
while (--i >= 0) {
string = acpi_ut_bound_string_output(string, end,
reversed_string[i]);
}
while (--width >= 0) {
string = acpi_ut_bound_string_output(string, end, ' ');
}
return (string);
}
/*******************************************************************************
*
* FUNCTION: vsnprintf
*
* PARAMETERS: string - String with boundary
* size - Boundary of the string
* format - Standard printf format
* args - Argument list
*
* RETURN: Number of bytes actually written.
*
* DESCRIPTION: Formatted output to a string using argument list pointer.
*
******************************************************************************/
int vsnprintf(char *string, acpi_size size, const char *format, va_list args)
{
u8 base;
u8 type;
s32 width;
s32 precision;
char qualifier;
u64 number;
char *pos;
char *end;
char c;
const char *s;
const void *p;
s32 length;
int i;
pos = string;
if (size != ACPI_UINT32_MAX) {
end = string + size;
} else {
end = ACPI_CAST_PTR(char, ACPI_UINT32_MAX);
}
for (; *format; ++format) {
if (*format != '%') {
pos = acpi_ut_bound_string_output(pos, end, *format);
continue;
}
type = 0;
base = 10;
/* Process sign */
do {
++format;
if (*format == '#') {
type |= ACPI_FORMAT_PREFIX;
} else if (*format == '0') {
type |= ACPI_FORMAT_ZERO;
} else if (*format == '+') {
type |= ACPI_FORMAT_SIGN_PLUS;
} else if (*format == ' ') {
type |= ACPI_FORMAT_SIGN_PLUS_SPACE;
} else if (*format == '-') {
type |= ACPI_FORMAT_LEFT;
} else {
break;
}
} while (1);
/* Process width */
width = -1;
if (isdigit((int)*format)) {
format = acpi_ut_scan_number(format, &number);
width = (s32)number;
} else if (*format == '*') {
++format;
width = va_arg(args, int);
if (width < 0) {
width = -width;
type |= ACPI_FORMAT_LEFT;
}
}
/* Process precision */
precision = -1;
if (*format == '.') {
++format;
if (isdigit((int)*format)) {
format = acpi_ut_scan_number(format, &number);
precision = (s32)number;
} else if (*format == '*') {
++format;
precision = va_arg(args, int);
}
if (precision < 0) {
precision = 0;
}
}
/* Process qualifier */
qualifier = -1;
if (*format == 'h' || *format == 'l' || *format == 'L') {
qualifier = *format;
++format;
if (qualifier == 'l' && *format == 'l') {
qualifier = 'L';
++format;
}
}
switch (*format) {
case '%':
pos = acpi_ut_bound_string_output(pos, end, '%');
continue;
case 'c':
if (!(type & ACPI_FORMAT_LEFT)) {
while (--width > 0) {
pos =
acpi_ut_bound_string_output(pos,
end,
' ');
}
}
c = (char)va_arg(args, int);
pos = acpi_ut_bound_string_output(pos, end, c);
while (--width > 0) {
pos =
acpi_ut_bound_string_output(pos, end, ' ');
}
continue;
case 's':
s = va_arg(args, char *);
if (!s) {
s = "<NULL>";
}
length = (s32)acpi_ut_bound_string_length(s, precision);
if (!(type & ACPI_FORMAT_LEFT)) {
while (length < width--) {
pos =
acpi_ut_bound_string_output(pos,
end,
' ');
}
}
for (i = 0; i < length; ++i) {
pos = acpi_ut_bound_string_output(pos, end, *s);
++s;
}
while (length < width--) {
pos =
acpi_ut_bound_string_output(pos, end, ' ');
}
continue;
case 'o':
base = 8;
break;
case 'X':
type |= ACPI_FORMAT_UPPER;
/* FALLTHROUGH */
case 'x':
base = 16;
break;
case 'd':
case 'i':
type |= ACPI_FORMAT_SIGN;
case 'u':
break;
case 'p':
if (width == -1) {
width = 2 * sizeof(void *);
type |= ACPI_FORMAT_ZERO;
}
p = va_arg(args, void *);
pos =
acpi_ut_format_number(pos, end, ACPI_TO_INTEGER(p),
16, width, precision, type);
continue;
default:
pos = acpi_ut_bound_string_output(pos, end, '%');
if (*format) {
pos =
acpi_ut_bound_string_output(pos, end,
*format);
} else {
--format;
}
continue;
}
if (qualifier == 'L') {
number = va_arg(args, u64);
if (type & ACPI_FORMAT_SIGN) {
number = (s64)number;
}
} else if (qualifier == 'l') {
number = va_arg(args, unsigned long);
if (type & ACPI_FORMAT_SIGN) {
number = (s32)number;
}
} else if (qualifier == 'h') {
number = (u16)va_arg(args, int);
if (type & ACPI_FORMAT_SIGN) {
number = (s16)number;
}
} else {
number = va_arg(args, unsigned int);
if (type & ACPI_FORMAT_SIGN) {
number = (signed int)number;
}
}
pos = acpi_ut_format_number(pos, end, number, base,
width, precision, type);
}
if (size > 0) {
if (pos < end) {
*pos = '\0';
} else {
end[-1] = '\0';
}
}
return ((int)ACPI_PTR_DIFF(pos, string));
}
/*******************************************************************************
*
* FUNCTION: snprintf
*
* PARAMETERS: string - String with boundary
* size - Boundary of the string
* Format, ... - Standard printf format
*
* RETURN: Number of bytes actually written.
*
* DESCRIPTION: Formatted output to a string.
*
******************************************************************************/
int snprintf(char *string, acpi_size size, const char *format, ...)
{
va_list args;
int length;
va_start(args, format);
length = vsnprintf(string, size, format, args);
va_end(args);
return (length);
}
/*******************************************************************************
*
* FUNCTION: sprintf
*
* PARAMETERS: string - String with boundary
* Format, ... - Standard printf format
*
* RETURN: Number of bytes actually written.
*
* DESCRIPTION: Formatted output to a string.
*
******************************************************************************/
int sprintf(char *string, const char *format, ...)
{
va_list args;
int length;
va_start(args, format);
length = vsnprintf(string, ACPI_UINT32_MAX, format, args);
va_end(args);
return (length);
}
#ifdef ACPI_APPLICATION
/*******************************************************************************
*
* FUNCTION: vprintf
*
* PARAMETERS: format - Standard printf format
* args - Argument list
*
* RETURN: Number of bytes actually written.
*
* DESCRIPTION: Formatted output to stdout using argument list pointer.
*
******************************************************************************/
int vprintf(const char *format, va_list args)
{
acpi_cpu_flags flags;
int length;
flags = acpi_os_acquire_lock(acpi_gbl_print_lock);
length = vsnprintf(acpi_gbl_print_buffer,
sizeof(acpi_gbl_print_buffer), format, args);
(void)fwrite(acpi_gbl_print_buffer, length, 1, ACPI_FILE_OUT);
acpi_os_release_lock(acpi_gbl_print_lock, flags);
return (length);
}
/*******************************************************************************
*
* FUNCTION: printf
*
* PARAMETERS: Format, ... - Standard printf format
*
* RETURN: Number of bytes actually written.
*
* DESCRIPTION: Formatted output to stdout.
*
******************************************************************************/
int printf(const char *format, ...)
{
va_list args;
int length;
va_start(args, format);
length = vprintf(format, args);
va_end(args);
return (length);
}
/*******************************************************************************
*
* FUNCTION: vfprintf
*
* PARAMETERS: file - File descriptor
* format - Standard printf format
* args - Argument list
*
* RETURN: Number of bytes actually written.
*
* DESCRIPTION: Formatted output to a file using argument list pointer.
*
******************************************************************************/
int vfprintf(FILE * file, const char *format, va_list args)
{
acpi_cpu_flags flags;
int length;
flags = acpi_os_acquire_lock(acpi_gbl_print_lock);
length = vsnprintf(acpi_gbl_print_buffer,
sizeof(acpi_gbl_print_buffer), format, args);
(void)fwrite(acpi_gbl_print_buffer, length, 1, file);
acpi_os_release_lock(acpi_gbl_print_lock, flags);
return (length);
}
/*******************************************************************************
*
* FUNCTION: fprintf
*
* PARAMETERS: file - File descriptor
* Format, ... - Standard printf format
*
* RETURN: Number of bytes actually written.
*
* DESCRIPTION: Formatted output to a file.
*
******************************************************************************/
int fprintf(FILE * file, const char *format, ...)
{
va_list args;
int length;
va_start(args, format);
length = vfprintf(file, format, args);
va_end(args);
return (length);
}
#endif