aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/test/Makefile.check1
-rw-r--r--core/test/run-console-log-buf-overrun.c113
-rw-r--r--libc/stdio/vsnprintf.c163
3 files changed, 222 insertions, 55 deletions
diff --git a/core/test/Makefile.check b/core/test/Makefile.check
index 03a6a8d..457b61c 100644
--- a/core/test/Makefile.check
+++ b/core/test/Makefile.check
@@ -2,6 +2,7 @@
CORE_TEST := core/test/run-device core/test/run-mem_region core/test/run-malloc core/test/run-malloc-speed core/test/run-mem_region_init core/test/run-mem_region_release_unused core/test/run-mem_region_release_unused_noalloc core/test/run-trace core/test/run-msg core/test/run-pel core/test/run-pool core/test/run-timer
CORE_TEST_NOSTUB := core/test/run-console-log
+CORE_TEST_NOSTUB += core/test/run-console-log-buf-overrun
LCOV_EXCLUDE += $(CORE_TEST:%=%.c) core/test/stubs.c
LCOV_EXCLUDE += $(CORE_TEST_NOSTUB:%=%.c) /usr/include/*
diff --git a/core/test/run-console-log-buf-overrun.c b/core/test/run-console-log-buf-overrun.c
new file mode 100644
index 0000000..eda99e2
--- /dev/null
+++ b/core/test/run-console-log-buf-overrun.c
@@ -0,0 +1,113 @@
+/* Copyright 2014-2015 IBM Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <config.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <stdarg.h>
+#include <compiler.h>
+
+#define __TEST__
+
+#define CHECK_BUF_ASSERT(buf, str) \
+ assert(memcmp(buf, str, strlen(str)) == 0)
+
+#define CHECK_ASSERT(str) \
+ CHECK_BUF_ASSERT(console_buffer, str)
+
+int huge_tb;
+
+static inline unsigned long mftb(void)
+{
+ /*
+ * return huge value for TB that overrun tmp[16] buffer defined
+ * in print_itoa().
+ */
+ if (huge_tb)
+ return 1223372515963611388;
+ else
+ return 42;
+}
+
+#include "../console-log.c"
+#include "../../libc/stdio/snprintf.c"
+#include "../../libc/stdio/vsnprintf.c"
+
+char console_buffer[4096];
+struct debug_descriptor debug_descriptor;
+
+bool flushed_to_drivers;
+
+ssize_t console_write(bool flush_to_drivers, const void *buf, size_t count)
+{
+ flushed_to_drivers = flush_to_drivers;
+ memcpy(console_buffer, buf, count);
+ return count;
+}
+
+int main(void)
+{
+ unsigned long value = 0xffffffffffffffff;
+ char *ptr = console_buffer;
+
+ debug_descriptor.console_log_levels = 0x75;
+
+ /* Test for huge TB value. */
+ huge_tb = 1;
+
+ prlog(PR_EMERG, "Hello World");
+ CHECK_ASSERT("[1223372515963611388,0] Hello World");
+
+ memset(console_buffer, 0, sizeof(console_buffer));
+
+ /* Test for normal TB with huge unsigned long value */
+ huge_tb = 0;
+
+ prlog(PR_EMERG, "Hello World %lu", value);
+ CHECK_ASSERT("[42,0] Hello World 18446744073709551615");
+
+ printf("Hello World %lu", value);
+ CHECK_ASSERT("[42,5] Hello World 18446744073709551615");
+
+ /*
+ * Test string of size > 320
+ *
+ * core/console-log.c:vprlog() uses buffer[320] to print message
+ * Try printing more than 320 bytes to test stack corruption.
+ * You would see Segmentation fault on stack corruption.
+ */
+ prlog(PR_EMERG, "%330s", "Hello World");
+
+ memset(console_buffer, 0, sizeof(console_buffer));
+
+ /*
+ * Test boundary condition.
+ *
+ * Print string of exact size 320. We should see string truncated
+ * with console_buffer[319] == '\0'.
+ */
+ memset(console_buffer, 0, sizeof(console_buffer));
+
+ prlog(PR_EMERG, "%313s", "Hello World");
+ assert(console_buffer[319] == 0);
+
+ /* compare truncated string */
+ ptr += 320 - strlen("Hello World");
+ CHECK_BUF_ASSERT(ptr, "Hello Worl");
+
+ return 0;
+}
diff --git a/libc/stdio/vsnprintf.c b/libc/stdio/vsnprintf.c
index b2f0b94..b9435b8 100644
--- a/libc/stdio/vsnprintf.c
+++ b/libc/stdio/vsnprintf.c
@@ -22,59 +22,99 @@ static const unsigned long long convert[] = {
0xFFFFFFFFFFULL, 0xFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL
};
+static int
+print_str_fill(char **buffer, size_t bufsize, char *sizec,
+ const char *str, char c)
+{
+ int i, sizei, len;
+ char *bstart = *buffer;
+ sizei = strtoul(sizec, NULL, 10);
+ len = strlen(str);
+ if (sizei > len) {
+ for (i = 0;
+ (i < (sizei - len)) && ((*buffer - bstart) < bufsize);
+ i++) {
+ **buffer = c;
+ *buffer += 1;
+ }
+ }
+ return 1;
+}
static int
-print_itoa(char **buffer, unsigned long value, unsigned short base, bool upper)
+print_str(char **buffer, size_t bufsize, const char *str)
+{
+ char *bstart = *buffer;
+ int i;
+
+ for (i = 0; (i < strlen(str)) && ((*buffer - bstart) < bufsize); i++) {
+ **buffer = str[i];
+ *buffer += 1;
+ }
+ return 1;
+}
+
+static unsigned int __attrconst
+print_intlen(unsigned long value, unsigned short int base)
+{
+ int i = 0;
+
+ while (value > 0) {
+ value /= base;
+ i++;
+ }
+ if (i == 0)
+ i = 1;
+ return i;
+}
+
+static int
+print_itoa(char **buffer, size_t bufsize, unsigned long value,
+ unsigned short base, bool upper)
{
const char zeichen[] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
char c;
- int i = 0;
- char tmp[16];
+ int i, len;
if(base <= 2 || base > 16)
return 0;
+ len = i = print_intlen(value, base);
+
+ /* Don't print to buffer if bufsize is not enough. */
+ if (len > bufsize)
+ return 0;
+
do {
c = zeichen[value % base];
if (upper)
c = toupper(c);
- tmp[i++] = c;
+
+ (*buffer)[--i] = c;
value /= base;
} while(value);
- while (i--) {
- **buffer = tmp[i];
- *buffer += 1;
- }
+ *buffer += len;
return 1;
}
-static unsigned int __attrconst
-print_intlen(unsigned long value, unsigned short int base)
-{
- int i = 0;
-
- while(value > 0) {
- value /= base;
- i++;
- }
- if(i == 0) i = 1;
- return i;
-}
-
static int
-print_fill(char **buffer, char *sizec, unsigned long size, unsigned short int base, char c, int optlen)
+print_fill(char **buffer, size_t bufsize, char *sizec, unsigned long size,
+ unsigned short int base, char c, int optlen)
{
int i, sizei, len;
+ char *bstart = *buffer;
sizei = strtoul(sizec, NULL, 10);
len = print_intlen(size, base) + optlen;
- if(sizei > len) {
- for(i = 0; i < (sizei - len); i++) {
+ if (sizei > len) {
+ for (i = 0;
+ (i < (sizei - len)) && ((*buffer - bstart) < bufsize);
+ i++) {
**buffer = c;
*buffer += 1;
}
@@ -85,10 +125,10 @@ print_fill(char **buffer, char *sizec, unsigned long size, unsigned short int ba
static int
-print_format(char **buffer, const char *format, void *var)
+print_format(char **buffer, size_t bufsize, const char *format, void *var)
{
- unsigned long start;
- unsigned int i = 0, sizei = 0, len = 0, length_mod = sizeof(int);
+ char *start;
+ unsigned int i = 0, length_mod = sizeof(int);
unsigned long value = 0;
unsigned long signBit;
char *form, sizec[32];
@@ -96,7 +136,7 @@ print_format(char **buffer, const char *format, void *var)
bool upper = false;
form = (char *) format;
- start = (unsigned long) *buffer;
+ start = *buffer;
form++;
if(*form == '0' || *form == '.') {
@@ -104,7 +144,7 @@ print_format(char **buffer, const char *format, void *var)
form++;
}
- while(*form != '\0') {
+ while ((*form != '\0') && ((*buffer - start) < bufsize)) {
switch(*form) {
case 'u':
case 'd':
@@ -117,53 +157,54 @@ print_format(char **buffer, const char *format, void *var)
*buffer += 1;
value = (-(unsigned long)value) & convert[length_mod];
}
- print_fill(buffer, sizec, value, 10, sign, 0);
- print_itoa(buffer, value, 10, upper);
+ print_fill(buffer, bufsize - (*buffer - start),
+ sizec, value, 10, sign, 0);
+ print_itoa(buffer, bufsize - (*buffer - start),
+ value, 10, upper);
break;
case 'X':
upper = true;
case 'x':
sizec[i] = '\0';
value = (unsigned long) var & convert[length_mod];
- print_fill(buffer, sizec, value, 16, sign, 0);
- print_itoa(buffer, value, 16, upper);
+ print_fill(buffer, bufsize - (*buffer - start),
+ sizec, value, 16, sign, 0);
+ print_itoa(buffer, bufsize - (*buffer - start),
+ value, 16, upper);
break;
case 'O':
case 'o':
sizec[i] = '\0';
value = (long int) var & convert[length_mod];
- print_fill(buffer, sizec, value, 8, sign, 0);
- print_itoa(buffer, value, 8, upper);
+ print_fill(buffer, bufsize - (*buffer - start),
+ sizec, value, 8, sign, 0);
+ print_itoa(buffer, bufsize - (*buffer - start),
+ value, 8, upper);
break;
case 'p':
sizec[i] = '\0';
- print_fill(buffer, sizec, (unsigned long) var, 16, ' ', 2);
- **buffer = '0';
- *buffer += 1;
- **buffer = 'x';
- *buffer += 1;
- print_itoa(buffer,(unsigned long) var, 16, upper);
+ print_fill(buffer, bufsize - (*buffer - start),
+ sizec, (unsigned long) var, 16, ' ', 2);
+ print_str(buffer, bufsize - (*buffer - start),
+ "0x");
+ print_itoa(buffer, bufsize - (*buffer - start),
+ (unsigned long) var, 16, upper);
break;
case 'c':
sizec[i] = '\0';
- print_fill(buffer, sizec, 1, 10, ' ', 0);
+ print_fill(buffer, bufsize - (*buffer - start),
+ sizec, 1, 10, ' ', 0);
**buffer = (unsigned long) var;
*buffer += 1;
break;
case 's':
sizec[i] = '\0';
- sizei = strtoul(sizec, NULL, 10);
- len = strlen((char *) var);
- if(sizei > len) {
- for(i = 0; i < (sizei - len); i++) {
- **buffer = ' ';
- *buffer += 1;
- }
- }
- for(i = 0; i < strlen((char *) var); i++) {
- **buffer = ((char *) var)[i];
- *buffer += 1;
- }
+ print_str_fill(buffer,
+ bufsize - (*buffer - start), sizec,
+ (char *) var, ' ');
+
+ print_str(buffer, bufsize - (*buffer - start),
+ (char *) var);
break;
case 'l':
form++;
@@ -210,6 +251,16 @@ vsnprintf(char *buffer, size_t bufsize, const char *format, va_list arg)
bstart = buffer;
ptr = (char *) format;
+ /*
+ * Return from here if size passed is zero, otherwise we would
+ * overrun buffer while setting NULL character at the end.
+ */
+ if (!buffer || !bufsize)
+ return 0;
+
+ /* Leave one space for NULL character */
+ bufsize--;
+
while(*ptr != '\0' && (buffer - bstart) < bufsize)
{
if(*ptr == '%') {
@@ -228,7 +279,9 @@ vsnprintf(char *buffer, size_t bufsize, const char *format, va_list arg)
if(*ptr == '%') {
*buffer++ = '%';
} else {
- print_format(&buffer, formstr, va_arg(arg, void *));
+ print_format(&buffer,
+ bufsize - (buffer - bstart),
+ formstr, va_arg(arg, void *));
}
ptr++;
} else {