From 8eda0cddc126d77545b9fcbb4aaafe4dfed08289 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaroslav=20=C5=A0karvada?= Date: Wed, 20 May 2026 15:23:50 +0200 Subject: [PATCH] Fixed stack buffer overflow in rrdcached and added more security checks Resolves: RHEL-173264 --- rrdtool-1.8.0-CVE-2026-43958.patch | 275 +++++++++++++++++++++++++++++ rrdtool.spec | 10 +- 2 files changed, 284 insertions(+), 1 deletion(-) create mode 100644 rrdtool-1.8.0-CVE-2026-43958.patch diff --git a/rrdtool-1.8.0-CVE-2026-43958.patch b/rrdtool-1.8.0-CVE-2026-43958.patch new file mode 100644 index 0000000..def2207 --- /dev/null +++ b/rrdtool-1.8.0-CVE-2026-43958.patch @@ -0,0 +1,275 @@ +diff --git a/src/rrd_daemon.c b/src/rrd_daemon.c +index 5831c1b..db9589c 100644 +--- a/src/rrd_daemon.c ++++ b/src/rrd_daemon.c +@@ -343,7 +343,13 @@ static void do_log( + pthread_mutex_lock(&log_lock); + time_t now = time(NULL); + +- strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", gmtime(&now)); ++ struct tm tm_buf; ++ ++ if (gmtime_r(&now, &tm_buf) == NULL) { ++ snprintf(buffer, sizeof(buffer), "(time error)"); ++ } else { ++ strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", &tm_buf); ++ } + fprintf(log_fh, "%s [%d] ", buffer, priority); + vfprintf(log_fh, format, args); + fprintf(log_fh, "\n"); +@@ -554,7 +560,7 @@ static int check_pidfile( + { + int pid_fd; + pid_t pid; +- char pid_str[16]; ++ char pid_str[16] = {0}; + + pid_fd = open_pidfile("open", O_RDWR); + if (pid_fd < 0) { +@@ -746,11 +752,7 @@ static int add_response_info( + return 0; /* no extra info returned when in BATCH */ + + va_start(argp, fmt); +-#ifdef HAVE_VSNPRINTF + len = vsnprintf(buffer, sizeof(buffer), fmt, argp); +-#else +- len = vsprintf(buffer, fmt, argp); +-#endif + va_end(argp); + if (len < 0) { + RRDD_LOG(LOG_ERR, "add_response_info: vnsprintf failed"); +@@ -839,14 +841,12 @@ static int send_response( + rc = RESP_OK; + } else { + rclen = snprintf(buffer, sizeof buffer, "%d ", lines); ++ if (rclen < 0 || rclen >= (int) sizeof(buffer)) ++ return -1; + } + + va_start(argp, fmt); +-#ifdef HAVE_VSNPRINTF + len = vsnprintf(buffer + rclen, sizeof(buffer) - rclen, fmt, argp); +-#else +- len = vsprintf(buffer + rclen, fmt, argp); +-#endif + va_end(argp); + if (len < 0) + return -1; +@@ -1829,7 +1829,7 @@ static int handle_request_tune( + goto done; + } + argc = atoi(i); +- if (argc < 0) { ++ if (argc <= 0) { + rc = send_response(sock, RESP_ERR, "Invalid argument count specified (%d)\n", + argc); + goto done; +@@ -1842,6 +1842,11 @@ static int handle_request_tune( + argc_tmp = 0; + while ((status = buffer_get_field(&buffer, &buffer_size, &tok)) == 0 + && tok) { ++ if (argc_tmp >= argc) { ++ rc = send_response(sock, RESP_ERR, ++ "Too many arguments (expected %d)\n", argc); ++ goto done; ++ } + argv[argc_tmp] = tok; + argc_tmp += 1; + } +@@ -2388,7 +2393,8 @@ static int handle_request_create( + char *file_copy = NULL, *dir = NULL, *dir2 = NULL; + char *tok; + int ac = 0; +- char *av[128]; ++#define MAX_CREATE_AV 128 ++ char *av[MAX_CREATE_AV]; + char **sources = NULL; + int sources_length = 0; + char *template = NULL; +@@ -2501,10 +2507,22 @@ static int handle_request_create( + continue; + } + if (!strncmp(tok, "DS:", 3)) { ++ if (ac >= MAX_CREATE_AV) { ++ rc = send_response(sock, RESP_ERR, ++ "Too many DS/RRA definitions (max %d)\n", ++ MAX_CREATE_AV); ++ goto done; ++ } + av[ac++] = tok; + continue; + } + if (!strncmp(tok, "RRA:", 4)) { ++ if (ac >= MAX_CREATE_AV) { ++ rc = send_response(sock, RESP_ERR, ++ "Too many DS/RRA definitions (max %d)\n", ++ MAX_CREATE_AV); ++ goto done; ++ } + av[ac++] = tok; + continue; + } +diff --git a/src/rrd_graph.c b/src/rrd_graph.c +index fa8b4c4..0d3577f 100644 +--- a/src/rrd_graph.c ++++ b/src/rrd_graph.c +@@ -2515,8 +2515,9 @@ int draw_horizontal_grid( + } + } + } else { +- sprintf(graph_label, im->primary_axis_format, +- scaledstep * (double) i, sisym); ++ snprintf(graph_label, sizeof(graph_label), ++ im->primary_axis_format, ++ scaledstep * (double) i, sisym); + } + } + break; +@@ -3858,11 +3859,14 @@ static cairo_status_t cairo_output( + { + image_desc_t *im = (image_desc_t *) closure; + +- im->rendered_image = +- (unsigned char *) realloc(im->rendered_image, +- im->rendered_image_size + length); +- if (im->rendered_image == NULL) +- return CAIRO_STATUS_WRITE_ERROR; ++ { ++ unsigned char *tmp = ++ (unsigned char *) realloc(im->rendered_image, ++ im->rendered_image_size + length); ++ if (tmp == NULL) ++ return CAIRO_STATUS_WRITE_ERROR; ++ im->rendered_image = tmp; ++ } + memcpy(im->rendered_image + im->rendered_image_size, data, length); + im->rendered_image_size += length; + return CAIRO_STATUS_SUCCESS; +@@ -4153,6 +4157,15 @@ int graph_paint_timestring( + (double *) malloc(sizeof(double) * im->xsize * 2); + int drawem = 0; + ++ if (foreY == NULL || foreX == NULL || ++ backY == NULL || backX == NULL) { ++ free(foreY); ++ free(foreX); ++ free(backY); ++ free(backX); ++ return -1; ++ } ++ + for (ii = 0; ii <= im->xsize; ii++) { + double ybase, ytop; + +@@ -5881,6 +5894,12 @@ int vdef_calc( + rrd_value_t *array; + int field; + ++ if (steps == 0) { ++ dst->vf.val = DNAN; ++ dst->vf.when = 0; ++ dst->vf.never = 1; ++ break; ++ } + if ((array = (rrd_value_t *) malloc(steps * sizeof(double))) == NULL) { + rrd_set_error("malloc VDEV_PERCENT"); + return -1; +@@ -5914,6 +5933,12 @@ int vdef_calc( + } + } + /* and allocate it */ ++ if (nancount == 0) { ++ dst->vf.val = DNAN; ++ dst->vf.when = 0; ++ dst->vf.never = 1; ++ break; ++ } + if ((array = + (rrd_value_t *) malloc(nancount * sizeof(double))) == NULL) { + rrd_set_error("malloc VDEV_PERCENT"); +diff --git a/src/rrd_graph_helper.c b/src/rrd_graph_helper.c +index dda0069..7a5683a 100644 +--- a/src/rrd_graph_helper.c ++++ b/src/rrd_graph_helper.c +@@ -1186,7 +1186,7 @@ static void legend_shift( + if (!legend || !legend[0]) { + return; + } +- memmove(legend + 2, legend, strlen(legend)); ++ memmove(legend + 2, legend, strlen(legend) + 1); + legend[0] = ' '; + legend[1] = ' '; + } +diff --git a/src/rrd_xport.c b/src/rrd_xport.c +index 405c9d8..bd07c3a 100644 +--- a/src/rrd_xport.c ++++ b/src/rrd_xport.c +@@ -357,6 +357,16 @@ static int rrd_xport_fn( + /* printf("step: %lu\n",*step); */ + free(step_list); + ++ if (*step == 0) { ++ rrd_set_error("xport step is zero"); ++ free(ref_list); ++ for (unsigned long k = *col_cnt; k > 0; k--) ++ free(legend_list[k - 1]); ++ *col_cnt = 0; ++ free(legend_list); ++ return (-1); ++ } ++ + *start = im->start - im->start % (*step); + + *end = im->end - im->end % (*step); +@@ -371,28 +381,30 @@ static int rrd_xport_fn( + (rrd_value_t *) malloc((*col_cnt) * row_cnt * + sizeof(rrd_value_t))) == NULL) { + free(ref_list); +- while ((*col_cnt)--) +- free(legend_list[*col_cnt]); ++ for (unsigned long k = *col_cnt; k > 0; k--) ++ free(legend_list[k - 1]); ++ *col_cnt = 0; + free(legend_list); + rrd_set_error("malloc xport data area"); + return (-1); + } + dstptr = (*data); + ++ long unsigned int chosen_idx = 0; + /* fill data structure */ +- for (dst_row = 0; (int) dst_row < (int) row_cnt; dst_row++) { +- for (i = 0; i < (int) (*col_cnt); i++) { ++ for (dst_row = 0; dst_row < row_cnt; dst_row++) { ++ for (i = 0; (unsigned long) i < *col_cnt; i++) { + long vidx = im->gdes[ref_list[i]].vidx; + time_t now = *start + dst_row * *step; + +- (*dstptr++) = im->gdes[vidx].data[(unsigned long) +- floor((double) +- (now - +- im->gdes[vidx].start) +- / im->gdes[vidx].step) +- * im->gdes[vidx].ds_cnt + +- im->gdes[vidx].ds]; ++ if (im->gdes[vidx].step > 0 && ++ now >= im->gdes[vidx].start) { ++ chosen_idx = floor((double) (now - im->gdes[vidx].start) / im->gdes[vidx].step) * im->gdes[vidx].ds_cnt + im->gdes[vidx].ds; + ++ (*dstptr++) = im->gdes[vidx].data[chosen_idx]; ++ } else { ++ (*dstptr++) = DNAN; ++ } + } + } + +@@ -975,6 +987,9 @@ static void escapeJSON( + size_t l = strlen(txt); + size_t pos = 0; + ++ if (tmp == NULL) ++ return; ++ + /* now iterate over the chars */ + for (size_t i = 0; (i < l) && (pos < len); i++, pos++) { + switch (txt[i]) { diff --git a/rrdtool.spec b/rrdtool.spec index 618bebe..8999b5e 100644 --- a/rrdtool.spec +++ b/rrdtool.spec @@ -18,7 +18,7 @@ Summary: Round Robin Database Tool to store and display time-series data Name: rrdtool Version: 1.8.0 -Release: 20%{?dist} +Release: 21%{?dist} # gd license in php bindings isn't by default built-in License: gpl-1.0-or-later AND gpl-2.0-or-later AND gpl-2.0-or-later WITH rrdtool-floss-exception-2.0 AND mit AND lgpl-2.0-or-later AND lgpl-2.1-or-later AND bsd-source-code AND snprintf AND bsd-3-clause AND gpl-2.0-only AND licenseref-fedora-public-domain AND gtkbook URL: https://oss.oetiker.ch/rrdtool/ @@ -37,6 +37,9 @@ Patch6: rrdtool-configure-c99.patch # gcc-14 fix # https://github.com/oetiker/rrdtool-1.x/commit/b76e3c578f1e9f582e9c28f50d82b1f569602075 Patch7: rrdtool-1.8.0-const-argv.patch +# backported from: +# https://github.com/oetiker/rrdtool-1.x/commit/4218ec7127ba6c7ea1c20d7c8ea6e2b3f83df73a +Patch8: rrdtool-1.8.0-CVE-2026-43958.patch BuildRequires: make BuildRequires: gcc-c++ @@ -187,6 +190,7 @@ The %{name}-lua package includes RRDtool bindings for Lua. %patch5 -p1 -b .BUILD_DATE-fix %patch6 -p1 -b .configure-c99 %patch7 -p1 -b .const-argv +%patch8 -p1 -b .CVE-2026-43958 # Fix to find correct python dir on lib64 perl -pi -e 's|get_python_lib\(0,0,prefix|get_python_lib\(1,0,prefix|g' \ @@ -415,6 +419,10 @@ LD_LIBRARY_PATH=%{buildroot}%{_libdir} php -n \ %endif %changelog +* Wed May 20 2026 Jaroslav Škarvada - 1.8.0-21 +- Fixed stack buffer overflow in rrdcached and added more security checks + Resolves: RHEL-173264 + * Tue Oct 29 2024 Troy Dawson - 1.8.0-20 - Bump release for October 2024 mass rebuild: Resolves: RHEL-64018