[GH-ISSUE #512] worksheet_data_validation_cell: heap overflow and infinite loop when value_list contains 256+ items #398

Open
opened 2026-05-05 12:14:43 -06:00 by gitea-mirror · 0 comments
Owner

Originally created by @billdenney on GitHub (Apr 6, 2026).
Original GitHub issue: https://github.com/jmcnamara/libxlsxwriter/issues/512

Summary

Passing a NULL-terminated value_list array with 256 or more entries to worksheet_data_validation_cell() (or worksheet_data_validation_range()) causes the process to hang indefinitely and ultimately overflow a heap buffer.

Reproduction

#include "xlsxwriter.h"
#include <string.h>

int main(void) {
    /* Build a NULL-terminated list of 256 single-character strings. */
    const char *list[257];
    char items[256][2];
    int i;

    for (i = 0; i < 256; i++) {
        items[i][0] = 'a' + (i % 26);
        items[i][1] = '\0';
        list[i] = items[i];
    }
    list[256] = NULL;

    lxw_workbook  *workbook  = workbook_new("validation_list_bug.xlsx");
    lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL);

    lxw_data_validation dv;
    memset(&dv, 0, sizeof(dv));
    dv.validate   = LXW_VALIDATION_TYPE_LIST;
    dv.value_list = list;

    /* This call never returns. */
    worksheet_data_validation_cell(worksheet, CELL("A1"), &dv);

    return workbook_close(workbook);
}

Compile with AddressSanitizer (-fsanitize=address) to get an immediate crash instead of a hang.

Expected behaviour

worksheet_data_validation_cell() should return LXW_ERROR_255_STRING_LENGTH_EXCEEDED. The combined CSV string for 256 single-character items is 511 characters (256 items + 255 commas), which exceeds Excel's 255-character limit for validation list strings.

Actual behaviour

The process crashes. With AddressSanitizer it crashes immediately with a heap-buffer-overflow report.

Root cause

The internal helper _validation_list_length() uses a uint8_t loop counter and exits early once the running total reaches LXW_VALIDATION_MAX_STRING_LENGTH (255). For 256 single-character items it exits after counting 128 of them (running total 256 ≥ 255 triggers the guard) and returns 255 after the length-- adjustment. The caller's check length > 255 evaluates to false, so the list is accepted.

_validation_list_to_csv() is then called. It also uses a uint8_t loop counter. After processing index 255, i++ wraps back to 0. list[0] is non-NULL, so the loop restarts from the beginning and runs forever, continuously calling strcat into a 1023-byte heap buffer until the buffer overflows and the heap is corrupted.

Suggested resolution

Two changes in src/worksheet.c.

In _validation_list_length(): change the counter type to size_t and remove the early-exit length guard so the function always returns the actual total length of the list. The caller's existing length > LXW_VALIDATION_MAX_STRING_LENGTH check then correctly rejects over-length lists before _validation_list_to_csv() is ever reached.

--- a/src/worksheet.c
+++ b/src/worksheet.c
@@ _validation_list_length()
-    uint8_t i = 0;
+    size_t i = 0;
     size_t length = 0;

     if (!list || !list[0])
         return 0;

-    while (list[i] && length < LXW_VALIDATION_MAX_STRING_LENGTH) {
+    while (list[i]) {
         /* Include commas in the length. */
         length += 1 + lxw_utf8_strlen(list[i]);
         i++;
     }

@@ _validation_list_to_csv()
-    uint8_t i = 0;
+    size_t i = 0;
Originally created by @billdenney on GitHub (Apr 6, 2026). Original GitHub issue: https://github.com/jmcnamara/libxlsxwriter/issues/512 ### Summary Passing a `NULL`-terminated `value_list` array with 256 or more entries to `worksheet_data_validation_cell()` (or `worksheet_data_validation_range()`) causes the process to hang indefinitely and ultimately overflow a heap buffer. ### Reproduction ```c #include "xlsxwriter.h" #include <string.h> int main(void) { /* Build a NULL-terminated list of 256 single-character strings. */ const char *list[257]; char items[256][2]; int i; for (i = 0; i < 256; i++) { items[i][0] = 'a' + (i % 26); items[i][1] = '\0'; list[i] = items[i]; } list[256] = NULL; lxw_workbook *workbook = workbook_new("validation_list_bug.xlsx"); lxw_worksheet *worksheet = workbook_add_worksheet(workbook, NULL); lxw_data_validation dv; memset(&dv, 0, sizeof(dv)); dv.validate = LXW_VALIDATION_TYPE_LIST; dv.value_list = list; /* This call never returns. */ worksheet_data_validation_cell(worksheet, CELL("A1"), &dv); return workbook_close(workbook); } ``` Compile with AddressSanitizer (`-fsanitize=address`) to get an immediate crash instead of a hang. ### Expected behaviour `worksheet_data_validation_cell()` should return `LXW_ERROR_255_STRING_LENGTH_EXCEEDED`. The combined CSV string for 256 single-character items is 511 characters (256 items + 255 commas), which exceeds Excel's 255-character limit for validation list strings. ### Actual behaviour The process crashes. With AddressSanitizer it crashes immediately with a heap-buffer-overflow report. ### Root cause The internal helper `_validation_list_length()` uses a `uint8_t` loop counter and exits early once the running total reaches `LXW_VALIDATION_MAX_STRING_LENGTH` (255). For 256 single-character items it exits after counting 128 of them (running total 256 ≥ 255 triggers the guard) and returns 255 after the `length--` adjustment. The caller's check `length > 255` evaluates to false, so the list is accepted. `_validation_list_to_csv()` is then called. It also uses a `uint8_t` loop counter. After processing index 255, `i++` wraps back to 0. `list[0]` is non-`NULL`, so the loop restarts from the beginning and runs forever, continuously calling `strcat` into a 1023-byte heap buffer until the buffer overflows and the heap is corrupted. ### Suggested resolution Two changes in `src/worksheet.c`. In `_validation_list_length()`: change the counter type to `size_t` and remove the early-exit length guard so the function always returns the actual total length of the list. The caller's existing `length > LXW_VALIDATION_MAX_STRING_LENGTH` check then correctly rejects over-length lists before `_validation_list_to_csv()` is ever reached. ```diff --- a/src/worksheet.c +++ b/src/worksheet.c @@ _validation_list_length() - uint8_t i = 0; + size_t i = 0; size_t length = 0; if (!list || !list[0]) return 0; - while (list[i] && length < LXW_VALIDATION_MAX_STRING_LENGTH) { + while (list[i]) { /* Include commas in the length. */ length += 1 + lxw_utf8_strlen(list[i]); i++; } @@ _validation_list_to_csv() - uint8_t i = 0; + size_t i = 0; ```
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: github-starred/libxlsxwriter#398
No description provided.