|
|
@@ -1022,12 +1022,23 @@ static wchar_t* fmt_to_wc_1(const char *src)
|
|
|
}
|
|
|
return dest;
|
|
|
}
|
|
|
+/* For a converted value: consumed at OUTPUT, which frees it there. */
|
|
|
# define fmt_to_wc(dest, src) \
|
|
|
dest = alloc[++allocno] = fmt_to_wc_1(src)
|
|
|
+/* For a converted format string: read through p until the level is popped, so
|
|
|
+ * it belongs to the level and not to OUTPUT. Freeing it at OUTPUT is a
|
|
|
+ * use-after-free -- the loop then reads *p out of the freed block. */
|
|
|
+# define stacked_to_wc(dest, src) \
|
|
|
+ do { \
|
|
|
+ if (fmt_alloc[lvl - 1] != NULL) \
|
|
|
+ free((void *)fmt_alloc[lvl - 1]); \
|
|
|
+ dest = fmt_alloc[lvl - 1] = fmt_to_wc_1(src); \
|
|
|
+ } while (0)
|
|
|
# define to_wc(dest, src) \
|
|
|
dest = fmt_to_wc_1(src)
|
|
|
#else
|
|
|
# define fmt_to_wc(dest, src) (dest) = (src)
|
|
|
+# define stacked_to_wc(dest, src) (dest) = (src)
|
|
|
# define to_wc(dest, src) (dest) = (src)
|
|
|
#endif
|
|
|
|
|
|
@@ -1048,6 +1059,10 @@ size_t __XL_NPP(strftime)(CHAR_T *__restrict s, size_t maxsize,
|
|
|
#if defined __UCLIBC_HAS_WCHAR__ && (defined L_wcsftime || defined L_wcsftime_l)
|
|
|
const CHAR_T *alloc[MAX_PUSH];
|
|
|
int allocno = -1;
|
|
|
+ /* A converted format string is read through p for as long as its stack
|
|
|
+ * level lives, so it cannot be freed at OUTPUT like a converted value.
|
|
|
+ * One slot per level, freed when the level is popped. */
|
|
|
+ const CHAR_T *fmt_alloc[MAX_PUSH];
|
|
|
#endif
|
|
|
size_t count;
|
|
|
size_t o_count;
|
|
|
@@ -1067,6 +1082,14 @@ size_t __XL_NPP(strftime)(CHAR_T *__restrict s, size_t maxsize,
|
|
|
|
|
|
LOOP:
|
|
|
if (!count) {
|
|
|
+#if defined __UCLIBC_HAS_WCHAR__ && (defined L_wcsftime || defined L_wcsftime_l)
|
|
|
+ /* Returning from inside an expansion, so the levels are never
|
|
|
+ * popped and have to be released here. */
|
|
|
+ while (lvl > 0) {
|
|
|
+ if (fmt_alloc[--lvl] != NULL)
|
|
|
+ free((void *)fmt_alloc[lvl]);
|
|
|
+ }
|
|
|
+#endif
|
|
|
return 0;
|
|
|
}
|
|
|
if (!*p) {
|
|
|
@@ -1075,6 +1098,12 @@ LOOP:
|
|
|
return maxsize - count;
|
|
|
}
|
|
|
p = stack[--lvl];
|
|
|
+#if defined __UCLIBC_HAS_WCHAR__ && (defined L_wcsftime || defined L_wcsftime_l)
|
|
|
+ if (fmt_alloc[lvl] != NULL) {
|
|
|
+ free((void *)fmt_alloc[lvl]);
|
|
|
+ fmt_alloc[lvl] = NULL;
|
|
|
+ }
|
|
|
+#endif
|
|
|
goto LOOP;
|
|
|
}
|
|
|
|
|
|
@@ -1120,31 +1149,41 @@ LOOP:
|
|
|
if (lvl == MAX_PUSH) {
|
|
|
goto OUTPUT; /* Stack full so treat as illegal spec. */
|
|
|
}
|
|
|
- stack[lvl++] = ++p;
|
|
|
+ stack[lvl] = ++p;
|
|
|
+#if defined __UCLIBC_HAS_WCHAR__ && (defined L_wcsftime || defined L_wcsftime_l)
|
|
|
+ fmt_alloc[lvl] = NULL;
|
|
|
+#endif
|
|
|
+ ++lvl;
|
|
|
if ((code &= 0xf) < 8) {
|
|
|
ccp = (const char *)(spec + STACKED_STRINGS_START + code);
|
|
|
ccp += *ccp;
|
|
|
- fmt_to_wc(p, ccp);
|
|
|
+ stacked_to_wc(p, ccp);
|
|
|
goto LOOP;
|
|
|
}
|
|
|
ccp = (const char *)spec + STACKED_STRINGS_NL_ITEM_START + (code & 7);
|
|
|
- fmt_to_wc(p, ccp);
|
|
|
+ stacked_to_wc(p, ccp);
|
|
|
#ifdef ENABLE_ERA_CODE
|
|
|
if ((mod & NO_E_MOD) /* Actually, this means E modifier present. */
|
|
|
&& (*(ccp = __XL_NPP(nl_langinfo)(_NL_ITEM(LC_TIME,
|
|
|
- (int)(((unsigned char *)p)[4]))
|
|
|
+ (int)(UCHAR_T)(p[4]))
|
|
|
__LOCALE_ARG
|
|
|
)))
|
|
|
) {
|
|
|
- fmt_to_wc(p, ccp);
|
|
|
+ stacked_to_wc(p, ccp);
|
|
|
goto LOOP;
|
|
|
}
|
|
|
#endif
|
|
|
+ /* The index is the value of the character, not its first
|
|
|
+ * byte: for wcsftime p is wchar_t*, so reading a byte gives
|
|
|
+ * the high one on a big-endian target -- always 0, which is
|
|
|
+ * ABDAY_1, and %c %x %X %r all returned "Sun". UCHAR_T is
|
|
|
+ * unsigned char in the narrow build and unsigned int in the
|
|
|
+ * wide one, which is exactly the widening wanted here. */
|
|
|
ccp = __XL_NPP(nl_langinfo)(_NL_ITEM(LC_TIME,
|
|
|
- (int)(*((unsigned char *)p)))
|
|
|
+ (int)(UCHAR_T)(*p))
|
|
|
__LOCALE_ARG
|
|
|
);
|
|
|
- fmt_to_wc(p, ccp);
|
|
|
+ stacked_to_wc(p, ccp);
|
|
|
goto LOOP;
|
|
|
}
|
|
|
|