Fix segmentation fault on strftime/1 and strflocaltime/1 (#3271)
When we use a large format string to `strftime/1` and `strflocaltime/1`, we get segmentation fault. This happens because the result buffer is allocated on the stack using `alloca` function, and `strftime` function writes out of the space when the size is not enough to format time. I fixed the segmentation fault issue by allocating the result buffer on the heap using `malloc` function, because `alloca` function does not provide a way to detect insufficient space.
This commit is contained in:
1 parent
dc849e9bb7
commit
4003202ccf
1 file changed
+18
-25
+18
-25
@@ -11,21 +11,6 @@
|
|||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#ifdef HAVE_ALLOCA_H
|
|
||||||
# include <alloca.h>
|
|
||||||
#elif !defined alloca
|
|
||||||
# ifdef __GNUC__
|
|
||||||
# define alloca __builtin_alloca
|
|
||||||
# elif defined _MSC_VER
|
|
||||||
# include <malloc.h>
|
|
||||||
# define alloca _alloca
|
|
||||||
# elif !defined HAVE_ALLOCA
|
|
||||||
# ifdef __cplusplus
|
|
||||||
extern "C"
|
|
||||||
# endif
|
|
||||||
void *alloca (size_t);
|
|
||||||
# endif
|
|
||||||
#endif
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
@@ -1768,8 +1753,8 @@ static jv f_strftime(jq_state *jq, jv a, jv b) {
|
|||||||
return ret_error(b, jv_string("strftime/1 requires parsed datetime inputs"));
|
return ret_error(b, jv_string("strftime/1 requires parsed datetime inputs"));
|
||||||
|
|
||||||
const char *fmt = jv_string_value(b);
|
const char *fmt = jv_string_value(b);
|
||||||
size_t alloced = strlen(fmt) + 100;
|
size_t max_size = strlen(fmt) + 100;
|
||||||
char *buf = alloca(alloced);
|
char *buf = jv_mem_alloc(max_size);
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
/* Apple Libc (as of version 1669.40.2) contains a bug which causes it to
|
/* Apple Libc (as of version 1669.40.2) contains a bug which causes it to
|
||||||
* ignore the `tm.tm_gmtoff` in favor of the global timezone. To print the
|
* ignore the `tm.tm_gmtoff` in favor of the global timezone. To print the
|
||||||
@@ -1777,7 +1762,7 @@ static jv f_strftime(jq_state *jq, jv a, jv b) {
|
|||||||
char *tz = (tz = getenv("TZ")) != NULL ? strdup(tz) : NULL;
|
char *tz = (tz = getenv("TZ")) != NULL ? strdup(tz) : NULL;
|
||||||
setenv("TZ", "UTC", 1);
|
setenv("TZ", "UTC", 1);
|
||||||
#endif
|
#endif
|
||||||
size_t n = strftime(buf, alloced, fmt, &tm);
|
size_t n = strftime(buf, max_size, fmt, &tm);
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
if (tz) {
|
if (tz) {
|
||||||
setenv("TZ", tz, 1);
|
setenv("TZ", tz, 1);
|
||||||
@@ -1788,9 +1773,13 @@ static jv f_strftime(jq_state *jq, jv a, jv b) {
|
|||||||
#endif
|
#endif
|
||||||
jv_free(b);
|
jv_free(b);
|
||||||
/* POSIX doesn't provide errno values for strftime() failures; weird */
|
/* POSIX doesn't provide errno values for strftime() failures; weird */
|
||||||
if (n == 0 || n > alloced)
|
if ((n == 0 && *fmt) || n > max_size) {
|
||||||
|
free(buf);
|
||||||
return jv_invalid_with_msg(jv_string("strftime/1: unknown system failure"));
|
return jv_invalid_with_msg(jv_string("strftime/1: unknown system failure"));
|
||||||
return jv_string(buf);
|
}
|
||||||
|
jv ret = jv_string_sized(buf, n);
|
||||||
|
free(buf);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
static jv f_strftime(jq_state *jq, jv a, jv b) {
|
static jv f_strftime(jq_state *jq, jv a, jv b) {
|
||||||
@@ -1813,14 +1802,18 @@ static jv f_strflocaltime(jq_state *jq, jv a, jv b) {
|
|||||||
if (!jv2tm(a, &tm, 1))
|
if (!jv2tm(a, &tm, 1))
|
||||||
return ret_error(b, jv_string("strflocaltime/1 requires parsed datetime inputs"));
|
return ret_error(b, jv_string("strflocaltime/1 requires parsed datetime inputs"));
|
||||||
const char *fmt = jv_string_value(b);
|
const char *fmt = jv_string_value(b);
|
||||||
size_t alloced = strlen(fmt) + 100;
|
size_t max_size = strlen(fmt) + 100;
|
||||||
char *buf = alloca(alloced);
|
char *buf = jv_mem_alloc(max_size);
|
||||||
size_t n = strftime(buf, alloced, fmt, &tm);
|
size_t n = strftime(buf, max_size, fmt, &tm);
|
||||||
jv_free(b);
|
jv_free(b);
|
||||||
/* POSIX doesn't provide errno values for strftime() failures; weird */
|
/* POSIX doesn't provide errno values for strftime() failures; weird */
|
||||||
if (n == 0 || n > alloced)
|
if ((n == 0 && *fmt) || n > max_size) {
|
||||||
|
free(buf);
|
||||||
return jv_invalid_with_msg(jv_string("strflocaltime/1: unknown system failure"));
|
return jv_invalid_with_msg(jv_string("strflocaltime/1: unknown system failure"));
|
||||||
return jv_string(buf);
|
}
|
||||||
|
jv ret = jv_string_sized(buf, n);
|
||||||
|
free(buf);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
static jv f_strflocaltime(jq_state *jq, jv a, jv b) {
|
static jv f_strflocaltime(jq_state *jq, jv a, jv b) {
|
||||||
|
|||||||
Reference in new issue
Block a user