Skip to content

Commit

Permalink
Fix aliasing-based undefined behavior in string functions.
Browse files Browse the repository at this point in the history
Copies these functions verbatim from musl v1.2.0.
  • Loading branch information
zekoz committed May 22, 2020
1 parent 5549e2a commit 3e15038
Show file tree
Hide file tree
Showing 9 changed files with 59 additions and 36 deletions.
5 changes: 5 additions & 0 deletions system/include/libc/features.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,9 @@
#define _Noreturn
#endif

#define weak __attribute__((__weak__))
#define hidden __attribute__((__visibility__("hidden")))
#define weak_alias(old, new) \
extern __typeof(old) new __attribute__((__weak__, __alias__(#old)))

#endif
9 changes: 6 additions & 3 deletions system/lib/libc/musl/src/string/memccpy.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,22 @@ void *memccpy(void *restrict dest, const void *restrict src, int c, size_t n)
{
unsigned char *d = dest;
const unsigned char *s = src;
size_t *wd, k;
const size_t *ws;

c = (unsigned char)c;
#ifdef __GNUC__
typedef size_t __attribute__((__may_alias__)) word;
word *wd;
const word *ws;
if (((uintptr_t)s & ALIGN) == ((uintptr_t)d & ALIGN)) {
for (; ((uintptr_t)s & ALIGN) && n && (*d=*s)!=c; n--, s++, d++);
if ((uintptr_t)s & ALIGN) goto tail;
k = ONES * c;
size_t k = ONES * c;
wd=(void *)d; ws=(const void *)s;
for (; n>=sizeof(size_t) && !HASZERO(*ws^k);
n-=sizeof(size_t), ws++, wd++) *wd = *ws;
d=(void *)wd; s=(const void *)ws;
}
#endif
for (; n && (*d=*s)!=c; n--, s++, d++);
tail:
if (*s==c) return d+1;
Expand Down
8 changes: 6 additions & 2 deletions system/lib/libc/musl/src/string/memchr.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,16 @@ void *memchr(const void *src, int c, size_t n)
{
const unsigned char *s = src;
c = (unsigned char)c;
#ifdef __GNUC__
for (; ((uintptr_t)s & ALIGN) && n && *s != c; s++, n--);
if (n && *s != c) {
const size_t *w;
typedef size_t __attribute__((__may_alias__)) word;
const word *w;
size_t k = ONES * c;
for (w = (const void *)s; n>=SS && !HASZERO(*w^k); w++, n-=SS);
for (s = (const void *)w; n && *s != c; s++, n--);
s = (const void *)w;
}
#endif
for (; n && *s != c; s++, n--);
return n ? (void *)s : 0;
}
10 changes: 8 additions & 2 deletions system/lib/libc/musl/src/string/memmove.c
Original file line number Diff line number Diff line change
@@ -1,34 +1,40 @@
#include <string.h>
#include <stdint.h>

#define WT size_t
#ifdef __GNUC__
typedef __attribute__((__may_alias__)) size_t WT;
#define WS (sizeof(WT))
#endif

void *memmove(void *dest, const void *src, size_t n)
{
char *d = dest;
const char *s = src;

if (d==s) return d;
if (s+n <= d || d+n <= s) return memcpy(d, s, n);
if ((uintptr_t)s-(uintptr_t)d-n <= -2*n) return memcpy(d, s, n);

if (d<s) {
#ifdef __GNUC__
if ((uintptr_t)s % WS == (uintptr_t)d % WS) {
while ((uintptr_t)d % WS) {
if (!n--) return dest;
*d++ = *s++;
}
for (; n>=WS; n-=WS, d+=WS, s+=WS) *(WT *)d = *(WT *)s;
}
#endif
for (; n; n--) *d++ = *s++;
} else {
#ifdef __GNUC__
if ((uintptr_t)s % WS == (uintptr_t)d % WS) {
while ((uintptr_t)(d+n) % WS) {
if (!n--) return dest;
d[n] = s[n];
}
while (n>=WS) n-=WS, *(WT *)(d+n) = *(WT *)(s+n);
}
#endif
while (n) n--, d[n] = s[n];
}

Expand Down
9 changes: 5 additions & 4 deletions system/lib/libc/musl/src/string/stpcpy.c
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
#include <string.h>
#include <stdint.h>
#include <limits.h>
#include "libc.h"

#define ALIGN (sizeof(size_t))
#define ONES ((size_t)-1/UCHAR_MAX)
Expand All @@ -10,16 +9,18 @@

char *__stpcpy(char *restrict d, const char *restrict s)
{
size_t *wd;
const size_t *ws;

#ifdef __GNUC__
typedef size_t __attribute__((__may_alias__)) word;
word *wd;
const word *ws;
if ((uintptr_t)s % ALIGN == (uintptr_t)d % ALIGN) {
for (; (uintptr_t)s % ALIGN; s++, d++)
if (!(*d=*s)) return d;
wd=(void *)d; ws=(const void *)s;
for (; !HASZERO(*ws); *wd++ = *ws++);
d=(void *)wd; s=(const void *)ws;
}
#endif
for (; (*d=*s); s++, d++);

return d;
Expand Down
30 changes: 14 additions & 16 deletions system/lib/libc/musl/src/string/stpncpy.c
Original file line number Diff line number Diff line change
@@ -1,31 +1,29 @@
#include <string.h>
#include <stdint.h>
#include <limits.h>
#include "libc.h"

#define ALIGN (sizeof(size_t)-1)
#define ALIGN (sizeof(size_t))
#define ONES ((size_t)-1/UCHAR_MAX)
#define HIGHS (ONES * (UCHAR_MAX/2+1))
#define HASZERO(x) ((x)-ONES & ~(x) & HIGHS)

char *__stpncpy(char *restrict d, const char *restrict s, size_t n)
char *__stpcpy(char *restrict d, const char *restrict s)
{
size_t *wd;
const size_t *ws;

if (((uintptr_t)s & ALIGN) == ((uintptr_t)d & ALIGN)) {
for (; ((uintptr_t)s & ALIGN) && n && (*d=*s); n--, s++, d++);
if (!n || !*s) goto tail;
#ifdef __GNUC__
typedef size_t __attribute__((__may_alias__)) word;
word *wd;
const word *ws;
if ((uintptr_t)s % ALIGN == (uintptr_t)d % ALIGN) {
for (; (uintptr_t)s % ALIGN; s++, d++)
if (!(*d=*s)) return d;
wd=(void *)d; ws=(const void *)s;
for (; n>=sizeof(size_t) && !HASZERO(*ws);
n-=sizeof(size_t), ws++, wd++) *wd = *ws;
for (; !HASZERO(*ws); *wd++ = *ws++);
d=(void *)wd; s=(const void *)ws;
}
for (; n && (*d=*s); n--, s++, d++);
tail:
memset(d, 0, n);
#endif
for (; (*d=*s); s++, d++);

return d;
}

weak_alias(__stpncpy, stpncpy);

weak_alias(__stpcpy, stpcpy);
12 changes: 7 additions & 5 deletions system/lib/libc/musl/src/string/strchrnul.c
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
#include <string.h>
#include <stdint.h>
#include <limits.h>
#include "libc.h"

#define ALIGN (sizeof(size_t))
#define ONES ((size_t)-1/UCHAR_MAX)
Expand All @@ -10,16 +9,19 @@

char *__strchrnul(const char *s, int c)
{
size_t *w, k;

c = (unsigned char)c;
if (!c) return (char *)s + strlen(s);

#ifdef __GNUC__
typedef size_t __attribute__((__may_alias__)) word;
const word *w;
for (; (uintptr_t)s % ALIGN; s++)
if (!*s || *(unsigned char *)s == c) return (char *)s;
k = ONES * c;
size_t k = ONES * c;
for (w = (void *)s; !HASZERO(*w) && !HASZERO(*w^k); w++);
for (s = (void *)w; *s && *(unsigned char *)s != c; s++);
s = (void *)w;
#endif
for (; *s && *(unsigned char *)s != c; s++);
return (char *)s;
}

Expand Down
8 changes: 6 additions & 2 deletions system/lib/libc/musl/src/string/strlen.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,13 @@
size_t strlen(const char *s)
{
const char *a = s;
const size_t *w;
#ifdef __GNUC__
typedef size_t __attribute__((__may_alias__)) word;
const word *w;
for (; (uintptr_t)s % ALIGN; s++) if (!*s) return s-a;
for (w = (const void *)s; !HASZERO(*w); w++);
for (s = (const void *)w; *s; s++);
s = (const void *)w;
#endif
for (; *s; s++);
return s-a;
}
4 changes: 2 additions & 2 deletions tests/code_size/random_printf_wasm2js.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"a.html": 19929,
"a.html": 19899,
"a.html.gz": 8344,
"total": 19929,
"total": 19899,
"total_gz": 8344
}

0 comments on commit 3e15038

Please sign in to comment.