summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorRich Felker <dalias@aerifal.cx>2013-11-26 20:01:21 -0500
committerRich Felker <dalias@aerifal.cx>2013-11-26 20:01:21 -0500
commitf63b8c8c455929f0f46cc017b4c675faeef901c4 (patch)
treea266c636db6c9eddd8b2c67b16ffee4245a640b4 /src
parent2b1f2f146d87fa20099c4d7080a07527dd19b165 (diff)
downloadmusl-f63b8c8c455929f0f46cc017b4c675faeef901c4.tar.gz
fix off-by-one length failure in strftime/wcsftime and improve error behavior
these functions were spuriously failing in the case where the buffer size was exactly the number of bytes/characters to be written, including null termination. since these functions do not have defined error conditions other than buffer size, a reasonable application may fail to check the return value when the format string and buffer size are known to be valid; such an application could then attempt to use a non-terminated buffer. in addition to fixing the bug, I have changed the error handling behavior so that these functions always null-terminate the output except in the case where the buffer size is zero, and so that they always write as many characters as possible before failing, rather than dropping whole fields that do not fit. this actually simplifies the logic somewhat anyway.
Diffstat (limited to 'src')
-rw-r--r--src/time/strftime.c14
-rw-r--r--src/time/wcsftime.c14
2 files changed, 16 insertions, 12 deletions
diff --git a/src/time/strftime.c b/src/time/strftime.c
index dac64037..bc150139 100644
--- a/src/time/strftime.c
+++ b/src/time/strftime.c
@@ -216,7 +216,7 @@ size_t __strftime_l(char *restrict s, size_t n, const char *restrict f, const st
const char *t;
int plus;
unsigned long width;
- for (l=0; l+1<n; f++) {
+ for (l=0; l<n; f++) {
if (!*f) {
s[l] = 0;
return l;
@@ -230,14 +230,13 @@ size_t __strftime_l(char *restrict s, size_t n, const char *restrict f, const st
width = strtoul(f, &p, 10);
if (*p == 'C' || *p == 'F' || *p == 'G' || *p == 'Y') {
if (!width && p!=f) width = 1;
- if (width >= n-l) return 0;
} else {
width = 0;
}
f = p;
if (*f == 'E' || *f == 'O') f++;
t = __strftime_fmt_1(&buf, &k, *f, tm, loc);
- if (!t) return 0;
+ if (!t) break;
if (width) {
for (; *t=='+' || *t=='-' || (*t=='0'&&t[1]); t++, k--);
width--;
@@ -247,14 +246,17 @@ size_t __strftime_l(char *restrict s, size_t n, const char *restrict f, const st
s[l++] = '-';
else
width++;
- if (width >= n-l) return 0;
- for (; width > k; width--)
+ for (; width > k && l < n; width--)
s[l++] = '0';
}
- if (k >= n-l) return 0;
+ if (k > n-l) k = n-l;
memcpy(s+l, t, k);
l += k;
}
+ if (n) {
+ if (l==n) l=n-1;
+ s[l] = 0;
+ }
return 0;
}
diff --git a/src/time/wcsftime.c b/src/time/wcsftime.c
index 72af9137..a2804ac8 100644
--- a/src/time/wcsftime.c
+++ b/src/time/wcsftime.c
@@ -16,7 +16,7 @@ size_t __wcsftime_l(wchar_t *restrict s, size_t n, const wchar_t *restrict f, co
const wchar_t *t;
int plus;
unsigned long width;
- for (l=0; l+1<n; f++) {
+ for (l=0; l<n; f++) {
if (!*f) {
s[l] = 0;
return l;
@@ -30,14 +30,13 @@ size_t __wcsftime_l(wchar_t *restrict s, size_t n, const wchar_t *restrict f, co
width = wcstoul(f, &p, 10);
if (*p == 'C' || *p == 'F' || *p == 'G' || *p == 'Y') {
if (!width && p!=f) width = 1;
- if (width >= n-l) return 0;
} else {
width = 0;
}
f = p;
if (*f == 'E' || *f == 'O') f++;
t_mb = __strftime_fmt_1(&buf, &k, *f, tm, loc);
- if (!t_mb) return 0;
+ if (!t_mb) break;
k = mbstowcs(wbuf, t_mb, sizeof wbuf / sizeof *wbuf);
if (k == (size_t)-1) return 0;
t = wbuf;
@@ -50,14 +49,17 @@ size_t __wcsftime_l(wchar_t *restrict s, size_t n, const wchar_t *restrict f, co
s[l++] = '-';
else
width++;
- if (width >= n-l) return 0;
- for (; width > k; width--)
+ for (; width > k && l < n; width--)
s[l++] = '0';
}
- if (k >= n-l) return 0;
+ if (k >= n-l) k = n-l;
wmemcpy(s+l, t, k);
l += k;
}
+ if (n) {
+ if (l==n) l=n-1;
+ s[l] = 0;
+ }
return 0;
}