svn commit: r367823 - head/lib/libc/string

Ed Maste emaste at FreeBSD.org
Thu Nov 19 00:03:16 UTC 2020


Author: emaste
Date: Thu Nov 19 00:03:15 2020
New Revision: 367823
URL: https://svnweb.freebsd.org/changeset/base/367823

Log:
  libc: fix undefined behavior from signed overflow in strstr and memmem
  
  unsigned char promotes to int, which can overflow when shifted left by
  24 bits or more. this has been reported multiple times but then
  forgotten. it's expected to be benign UB, but can trap when built with
  explicit overflow catching (ubsan or similar). fix it now.
  
  note that promotion to uint32_t is safe and portable even outside of
  the assumptions usually made in musl, since either uint32_t has rank
  at least unsigned int, so that no further default promotions happen,
  or int is wide enough that the shift can't overflow. this is a
  desirable property to have in case someone wants to reuse the code
  elsewhere.
  
  musl commit: 593caa456309714402ca4cb77c3770f4c24da9da
  
  Obtained from:	musl

Modified:
  head/lib/libc/string/memmem.c
  head/lib/libc/string/strstr.c

Modified: head/lib/libc/string/memmem.c
==============================================================================
--- head/lib/libc/string/memmem.c	Thu Nov 19 00:02:12 2020	(r367822)
+++ head/lib/libc/string/memmem.c	Thu Nov 19 00:03:15 2020	(r367823)
@@ -41,8 +41,8 @@ twobyte_memmem(const unsigned char *h, size_t k, const
 static char *
 threebyte_memmem(const unsigned char *h, size_t k, const unsigned char *n)
 {
-	uint32_t nw = n[0] << 24 | n[1] << 16 | n[2] << 8;
-	uint32_t hw = h[0] << 24 | h[1] << 16 | h[2] << 8;
+	uint32_t nw = (uint32_t)n[0] << 24 | n[1] << 16 | n[2] << 8;
+	uint32_t hw = (uint32_t)h[0] << 24 | h[1] << 16 | h[2] << 8;
 	for (h += 3, k -= 3; k; k--, hw = (hw | *h++) << 8)
 		if (hw == nw)
 			return (char *)h - 3;
@@ -52,8 +52,8 @@ threebyte_memmem(const unsigned char *h, size_t k, con
 static char *
 fourbyte_memmem(const unsigned char *h, size_t k, const unsigned char *n)
 {
-	uint32_t nw = n[0] << 24 | n[1] << 16 | n[2] << 8 | n[3];
-	uint32_t hw = h[0] << 24 | h[1] << 16 | h[2] << 8 | h[3];
+	uint32_t nw = (uint32_t)n[0] << 24 | n[1] << 16 | n[2] << 8 | n[3];
+	uint32_t hw = (uint32_t)h[0] << 24 | h[1] << 16 | h[2] << 8 | h[3];
 	for (h += 4, k -= 4; k; k--, hw = hw << 8 | *h++)
 		if (hw == nw)
 			return (char *)h - 4;

Modified: head/lib/libc/string/strstr.c
==============================================================================
--- head/lib/libc/string/strstr.c	Thu Nov 19 00:02:12 2020	(r367822)
+++ head/lib/libc/string/strstr.c	Thu Nov 19 00:03:15 2020	(r367823)
@@ -40,8 +40,8 @@ twobyte_strstr(const unsigned char *h, const unsigned 
 static char *
 threebyte_strstr(const unsigned char *h, const unsigned char *n)
 {
-	uint32_t nw = n[0] << 24 | n[1] << 16 | n[2] << 8;
-	uint32_t hw = h[0] << 24 | h[1] << 16 | h[2] << 8;
+	uint32_t nw = (uint32_t)n[0] << 24 | n[1] << 16 | n[2] << 8;
+	uint32_t hw = (uint32_t)h[0] << 24 | h[1] << 16 | h[2] << 8;
 	for (h += 2; *h && hw != nw; hw = (hw | *++h) << 8)
 		;
 	return *h ? (char *)h - 2 : 0;
@@ -50,8 +50,8 @@ threebyte_strstr(const unsigned char *h, const unsigne
 static char *
 fourbyte_strstr(const unsigned char *h, const unsigned char *n)
 {
-	uint32_t nw = n[0] << 24 | n[1] << 16 | n[2] << 8 | n[3];
-	uint32_t hw = h[0] << 24 | h[1] << 16 | h[2] << 8 | h[3];
+	uint32_t nw = (uint32_t)n[0] << 24 | n[1] << 16 | n[2] << 8 | n[3];
+	uint32_t hw = (uint32_t)h[0] << 24 | h[1] << 16 | h[2] << 8 | h[3];
 	for (h += 3; *h && hw != nw; hw = hw << 8 | *++h)
 		;
 	return *h ? (char *)h - 3 : 0;


More information about the svn-src-all mailing list