From b05ec5ae933987e6b4a4f6a318cb13e035ad33e9 Mon Sep 17 00:00:00 2001
From: Elliott Hughes <enh@google.com>
Date: Tue, 23 Sep 2014 14:53:10 -0700
Subject: [PATCH] Pull in upstream fixes to reject invalid bases.

Also add tests to make sure the full set works correctly.

Change-Id: I3e7f237f12c9c93e1185a97c9717803e7e55a73c
---
 .../lib/libc/stdlib/strtoimax.c               | 16 ++++-
 .../upstream-openbsd/lib/libc/stdlib/strtol.c |  7 +-
 .../lib/libc/stdlib/strtoll.c                 | 15 +++-
 .../lib/libc/stdlib/strtoul.c                 | 11 ++-
 .../lib/libc/stdlib/strtoull.c                | 15 ++--
 .../lib/libc/stdlib/strtoumax.c               | 16 +++--
 tests/inttypes_test.cpp                       | 55 +++++++++++++-
 tests/stdlib_test.cpp                         | 48 +++++++++++++
 tests/wchar_test.cpp                          | 72 +++++++++++++++++++
 9 files changed, 232 insertions(+), 23 deletions(-)

diff --git a/libc/upstream-openbsd/lib/libc/stdlib/strtoimax.c b/libc/upstream-openbsd/lib/libc/stdlib/strtoimax.c
index 2c77f4165..2fc04e485 100644
--- a/libc/upstream-openbsd/lib/libc/stdlib/strtoimax.c
+++ b/libc/upstream-openbsd/lib/libc/stdlib/strtoimax.c
@@ -1,6 +1,5 @@
-/*	$OpenBSD: strtoimax.c,v 1.1 2006/01/13 17:58:09 millert Exp $	*/
-
-/*-
+/*	$OpenBSD: strtoimax.c,v 1.2 2014/09/13 20:10:12 schwarze Exp $	*/
+/*
  * Copyright (c) 1992 The Regents of the University of California.
  * All rights reserved.
  *
@@ -47,6 +46,17 @@ strtoimax(const char *nptr, char **endptr, int base)
 	int c;
 	int neg, any, cutlim;
 
+	/*
+	 * Ensure that base is between 2 and 36 inclusive, or the special
+	 * value of 0.
+	 */
+	if (base < 0 || base == 1 || base > 36) {
+		if (endptr != 0)
+			*endptr = (char *)nptr;
+		errno = EINVAL;
+		return 0;
+	}
+
 	/*
 	 * Skip white space and pick up leading +/- sign if any.
 	 * If base is 0, allow 0x for hex and 0 for octal, else
diff --git a/libc/upstream-openbsd/lib/libc/stdlib/strtol.c b/libc/upstream-openbsd/lib/libc/stdlib/strtol.c
index dc2cf8871..86cec3508 100644
--- a/libc/upstream-openbsd/lib/libc/stdlib/strtol.c
+++ b/libc/upstream-openbsd/lib/libc/stdlib/strtol.c
@@ -1,5 +1,5 @@
-/*	$OpenBSD: strtol.c,v 1.9 2013/04/17 17:40:35 tedu Exp $ */
-/*-
+/*	$OpenBSD: strtol.c,v 1.10 2014/09/13 20:10:12 schwarze Exp $ */
+/*
  * Copyright (c) 1990 The Regents of the University of California.
  * All rights reserved.
  *
@@ -33,7 +33,6 @@
 #include <limits.h>
 #include <stdlib.h>
 
-
 /*
  * Convert a string to a long integer.
  *
@@ -52,7 +51,7 @@ strtol(const char *nptr, char **endptr, int base)
 	 * Ensure that base is between 2 and 36 inclusive, or the special
 	 * value of 0.
 	 */
-	if (base != 0 && (base < 2 || base > 36)) {
+	if (base < 0 || base == 1 || base > 36) {
 		if (endptr != 0)
 			*endptr = (char *)nptr;
 		errno = EINVAL;
diff --git a/libc/upstream-openbsd/lib/libc/stdlib/strtoll.c b/libc/upstream-openbsd/lib/libc/stdlib/strtoll.c
index 4bcc5565b..cf82c8e1a 100644
--- a/libc/upstream-openbsd/lib/libc/stdlib/strtoll.c
+++ b/libc/upstream-openbsd/lib/libc/stdlib/strtoll.c
@@ -1,5 +1,5 @@
-/* $OpenBSD: strtoll.c,v 1.7 2013/03/28 18:09:38 martynas Exp $ */
-/*-
+/*	$OpenBSD: strtoll.c,v 1.8 2014/09/13 20:10:12 schwarze Exp $ */
+/*
  * Copyright (c) 1992 The Regents of the University of California.
  * All rights reserved.
  *
@@ -49,6 +49,17 @@ strtoll(const char *nptr, char **endptr, int base)
 	int c;
 	int neg, any, cutlim;
 
+	/*
+	 * Ensure that base is between 2 and 36 inclusive, or the special
+	 * value of 0.
+	 */
+	if (base < 0 || base == 1 || base > 36) {
+		if (endptr != 0)
+			*endptr = (char *)nptr;
+		errno = EINVAL;
+		return 0;
+	}
+
 	/*
 	 * Skip white space and pick up leading +/- sign if any.
 	 * If base is 0, allow 0x for hex and 0 for octal, else
diff --git a/libc/upstream-openbsd/lib/libc/stdlib/strtoul.c b/libc/upstream-openbsd/lib/libc/stdlib/strtoul.c
index a236365d2..2aa41b76e 100644
--- a/libc/upstream-openbsd/lib/libc/stdlib/strtoul.c
+++ b/libc/upstream-openbsd/lib/libc/stdlib/strtoul.c
@@ -1,6 +1,6 @@
-/*	$OpenBSD: strtoul.c,v 1.8 2013/04/17 17:40:35 tedu Exp $ */
+/*	$OpenBSD: strtoul.c,v 1.9 2014/09/13 20:10:12 schwarze Exp $ */
 /*
- * Copyright (c) 1990 Regents of the University of California.
+ * Copyright (c) 1990 The Regents of the University of California.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -50,6 +50,13 @@ strtoul(const char *nptr, char **endptr, int base)
 	/*
 	 * See strtol for comments as to the logic used.
 	 */
+	if (base < 0 || base == 1 || base > 36) {
+		if (endptr != 0)
+			*endptr = (char *)nptr;
+		errno = EINVAL;
+		return 0;
+	}
+
 	s = nptr;
 	do {
 		c = (unsigned char) *s++;
diff --git a/libc/upstream-openbsd/lib/libc/stdlib/strtoull.c b/libc/upstream-openbsd/lib/libc/stdlib/strtoull.c
index 28f613a08..846417630 100644
--- a/libc/upstream-openbsd/lib/libc/stdlib/strtoull.c
+++ b/libc/upstream-openbsd/lib/libc/stdlib/strtoull.c
@@ -1,5 +1,5 @@
-/*	$OpenBSD: strtoull.c,v 1.6 2013/03/28 18:09:38 martynas Exp $ */
-/*-
+/*	$OpenBSD: strtoull.c,v 1.7 2014/09/13 20:10:12 schwarze Exp $ */
+/*
  * Copyright (c) 1992 The Regents of the University of California.
  * All rights reserved.
  *
@@ -50,8 +50,15 @@ strtoull(const char *nptr, char **endptr, int base)
 	int neg, any, cutlim;
 
 	/*
-	 * See strtoq for comments as to the logic used.
+	 * See strtoll for comments as to the logic used.
 	 */
+	if (base < 0 || base == 1 || base > 36) {
+		if (endptr != 0)
+			*endptr = (char *)nptr;
+		errno = EINVAL;
+		return 0;
+	}
+
 	s = nptr;
 	do {
 		c = (unsigned char) *s++;
@@ -59,7 +66,7 @@ strtoull(const char *nptr, char **endptr, int base)
 	if (c == '-') {
 		neg = 1;
 		c = *s++;
-	} else { 
+	} else {
 		neg = 0;
 		if (c == '+')
 			c = *s++;
diff --git a/libc/upstream-openbsd/lib/libc/stdlib/strtoumax.c b/libc/upstream-openbsd/lib/libc/stdlib/strtoumax.c
index ce6e2c00f..c73f7e507 100644
--- a/libc/upstream-openbsd/lib/libc/stdlib/strtoumax.c
+++ b/libc/upstream-openbsd/lib/libc/stdlib/strtoumax.c
@@ -1,6 +1,5 @@
-/*	$OpenBSD: strtoumax.c,v 1.1 2006/01/13 17:58:09 millert Exp $	*/
-
-/*-
+/*	$OpenBSD: strtoumax.c,v 1.2 2014/09/13 20:10:12 schwarze Exp $	*/
+/*
  * Copyright (c) 1992 The Regents of the University of California.
  * All rights reserved.
  *
@@ -48,8 +47,15 @@ strtoumax(const char *nptr, char **endptr, int base)
 	int neg, any, cutlim;
 
 	/*
-	 * See strtoq for comments as to the logic used.
+	 * See strtoimax for comments as to the logic used.
 	 */
+	if (base < 0 || base == 1 || base > 36) {
+		if (endptr != 0)
+			*endptr = (char *)nptr;
+		errno = EINVAL;
+		return 0;
+	}
+
 	s = nptr;
 	do {
 		c = (unsigned char) *s++;
@@ -57,7 +63,7 @@ strtoumax(const char *nptr, char **endptr, int base)
 	if (c == '-') {
 		neg = 1;
 		c = *s++;
-	} else { 
+	} else {
 		neg = 0;
 		if (c == '+')
 			c = *s++;
diff --git a/tests/inttypes_test.cpp b/tests/inttypes_test.cpp
index e58850340..dbbb6d4ca 100644
--- a/tests/inttypes_test.cpp
+++ b/tests/inttypes_test.cpp
@@ -14,11 +14,12 @@
  * limitations under the License.
  */
 
-#include <gtest/gtest.h>
-
-#include <stdio.h>
 #include <inttypes.h>
 
+#include <errno.h>
+#include <gtest/gtest.h>
+#include <stdio.h>
+
 TEST(inttypes, misc) {
   char buf[512];
 
@@ -46,3 +47,51 @@ TEST(inttypes, wcstoimax) {
 TEST(inttypes, wcstoumax) {
   ASSERT_EQ(123U, wcstoumax(L"123", NULL, 10));
 }
+
+TEST(inttypes, strtoimax_EINVAL) {
+  errno = 0;
+  strtoimax("123", NULL, -1);
+  ASSERT_EQ(EINVAL, errno);
+  errno = 0;
+  strtoimax("123", NULL, 1);
+  ASSERT_EQ(EINVAL, errno);
+  errno = 0;
+  strtoimax("123", NULL, 37);
+  ASSERT_EQ(EINVAL, errno);
+}
+
+TEST(inttypes, strtoumax_EINVAL) {
+  errno = 0;
+  strtoumax("123", NULL, -1);
+  ASSERT_EQ(EINVAL, errno);
+  errno = 0;
+  strtoumax("123", NULL, 1);
+  ASSERT_EQ(EINVAL, errno);
+  errno = 0;
+  strtoumax("123", NULL, 37);
+  ASSERT_EQ(EINVAL, errno);
+}
+
+TEST(inttypes, wcstoimax_EINVAL) {
+  errno = 0;
+  wcstoimax(L"123", NULL, -1);
+  ASSERT_EQ(EINVAL, errno);
+  errno = 0;
+  wcstoimax(L"123", NULL, 1);
+  ASSERT_EQ(EINVAL, errno);
+  errno = 0;
+  wcstoimax(L"123", NULL, 37);
+  ASSERT_EQ(EINVAL, errno);
+}
+
+TEST(inttypes, wcstoumax_EINVAL) {
+  errno = 0;
+  wcstoumax(L"123", NULL, -1);
+  ASSERT_EQ(EINVAL, errno);
+  errno = 0;
+  wcstoumax(L"123", NULL, 1);
+  ASSERT_EQ(EINVAL, errno);
+  errno = 0;
+  wcstoumax(L"123", NULL, 37);
+  ASSERT_EQ(EINVAL, errno);
+}
diff --git a/tests/stdlib_test.cpp b/tests/stdlib_test.cpp
index 553f018ed..667ccd6a4 100644
--- a/tests/stdlib_test.cpp
+++ b/tests/stdlib_test.cpp
@@ -361,3 +361,51 @@ TEST(stdlib, unlockpt_ENOTTY) {
   ASSERT_EQ(ENOTTY, errno);
   close(fd);
 }
+
+TEST(stdlib, strtol_EINVAL) {
+  errno = 0;
+  strtol("123", NULL, -1);
+  ASSERT_EQ(EINVAL, errno);
+  errno = 0;
+  strtol("123", NULL, 1);
+  ASSERT_EQ(EINVAL, errno);
+  errno = 0;
+  strtol("123", NULL, 37);
+  ASSERT_EQ(EINVAL, errno);
+}
+
+TEST(stdlib, strtoll_EINVAL) {
+  errno = 0;
+  strtoll("123", NULL, -1);
+  ASSERT_EQ(EINVAL, errno);
+  errno = 0;
+  strtoll("123", NULL, 1);
+  ASSERT_EQ(EINVAL, errno);
+  errno = 0;
+  strtoll("123", NULL, 37);
+  ASSERT_EQ(EINVAL, errno);
+}
+
+TEST(stdlib, strtoul_EINVAL) {
+  errno = 0;
+  strtoul("123", NULL, -1);
+  ASSERT_EQ(EINVAL, errno);
+  errno = 0;
+  strtoul("123", NULL, 1);
+  ASSERT_EQ(EINVAL, errno);
+  errno = 0;
+  strtoul("123", NULL, 37);
+  ASSERT_EQ(EINVAL, errno);
+}
+
+TEST(stdlib, strtoull_EINVAL) {
+  errno = 0;
+  strtoull("123", NULL, -1);
+  ASSERT_EQ(EINVAL, errno);
+  errno = 0;
+  strtoull("123", NULL, 1);
+  ASSERT_EQ(EINVAL, errno);
+  errno = 0;
+  strtoull("123", NULL, 37);
+  ASSERT_EQ(EINVAL, errno);
+}
diff --git a/tests/wchar_test.cpp b/tests/wchar_test.cpp
index 760475fa1..887266cfa 100644
--- a/tests/wchar_test.cpp
+++ b/tests/wchar_test.cpp
@@ -520,3 +520,75 @@ TEST(stdio, open_wmemstream_EINVAL) {
   GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif
 }
+
+TEST(wchar, wcstol_EINVAL) {
+  errno = 0;
+  wcstol(L"123", NULL, -1);
+  ASSERT_EQ(EINVAL, errno);
+  errno = 0;
+  wcstol(L"123", NULL, 1);
+  ASSERT_EQ(EINVAL, errno);
+  errno = 0;
+  wcstol(L"123", NULL, 37);
+  ASSERT_EQ(EINVAL, errno);
+}
+
+TEST(wchar, wcstoll_EINVAL) {
+  errno = 0;
+  wcstoll(L"123", NULL, -1);
+  ASSERT_EQ(EINVAL, errno);
+  errno = 0;
+  wcstoll(L"123", NULL, 1);
+  ASSERT_EQ(EINVAL, errno);
+  errno = 0;
+  wcstoll(L"123", NULL, 37);
+  ASSERT_EQ(EINVAL, errno);
+}
+
+TEST(wchar, wcstoul_EINVAL) {
+  errno = 0;
+  wcstoul(L"123", NULL, -1);
+  ASSERT_EQ(EINVAL, errno);
+  errno = 0;
+  wcstoul(L"123", NULL, 1);
+  ASSERT_EQ(EINVAL, errno);
+  errno = 0;
+  wcstoul(L"123", NULL, 37);
+  ASSERT_EQ(EINVAL, errno);
+}
+
+TEST(wchar, wcstoull_EINVAL) {
+  errno = 0;
+  wcstoull(L"123", NULL, -1);
+  ASSERT_EQ(EINVAL, errno);
+  errno = 0;
+  wcstoull(L"123", NULL, 1);
+  ASSERT_EQ(EINVAL, errno);
+  errno = 0;
+  wcstoull(L"123", NULL, 37);
+  ASSERT_EQ(EINVAL, errno);
+}
+
+TEST(wchar, wcstoll_l_EINVAL) {
+  errno = 0;
+  wcstoll_l(L"123", NULL, -1, LC_GLOBAL_LOCALE);
+  ASSERT_EQ(EINVAL, errno);
+  errno = 0;
+  wcstoll_l(L"123", NULL, 1, LC_GLOBAL_LOCALE);
+  ASSERT_EQ(EINVAL, errno);
+  errno = 0;
+  wcstoll_l(L"123", NULL, 37, LC_GLOBAL_LOCALE);
+  ASSERT_EQ(EINVAL, errno);
+}
+
+TEST(wchar, wcstoull_l_EINVAL) {
+  errno = 0;
+  wcstoull_l(L"123", NULL, -1, LC_GLOBAL_LOCALE);
+  ASSERT_EQ(EINVAL, errno);
+  errno = 0;
+  wcstoull_l(L"123", NULL, 1, LC_GLOBAL_LOCALE);
+  ASSERT_EQ(EINVAL, errno);
+  errno = 0;
+  wcstoull_l(L"123", NULL, 37, LC_GLOBAL_LOCALE);
+  ASSERT_EQ(EINVAL, errno);
+}