Skip to content

Commit

Permalink
worksheet: fix heap buffer overflows from empty strings
Browse files Browse the repository at this point in the history
Closes #446
  • Loading branch information
jmcnamara committed May 24, 2024
1 parent 12d67a6 commit c89c551
Show file tree
Hide file tree
Showing 7 changed files with 74 additions and 17 deletions.
4 changes: 2 additions & 2 deletions include/xlsxwriter/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,8 @@ typedef enum lxw_error {
/** Function parameter validation error. */
LXW_ERROR_PARAMETER_VALIDATION,

/** Worksheet name cannot be blank. */
LXW_ERROR_SHEETNAME_IS_BLANK,
/** Function string parameter is empty. */
LXW_ERROR_PARAMETER_IS_EMPTY,

/** Worksheet name exceeds Excel's limit of 31 characters. */
LXW_ERROR_SHEETNAME_LENGTH_EXCEEDED,
Expand Down
3 changes: 1 addition & 2 deletions include/xlsxwriter/utility.h
Original file line number Diff line number Diff line change
Expand Up @@ -221,10 +221,9 @@ double lxw_unixtime_to_excel_date_epoch(int64_t unixtime, uint8_t date_1904);

char *lxw_strdup(const char *str);
char *lxw_strdup_formula(const char *formula);

size_t lxw_utf8_strlen(const char *str);

void lxw_str_tolower(char *str);
uint8_t lxw_str_is_empty(const char *str);

/* Define a portable version of strcasecmp(). */
#ifdef _MSC_VER
Expand Down
2 changes: 1 addition & 1 deletion src/chart.c
Original file line number Diff line number Diff line change
Expand Up @@ -480,7 +480,7 @@ _chart_axis_set_default_num_format(lxw_chart_axis *axis, char *num_format)
}

/*
* Verify that a X/Y error bar property is support for the chart type.
* Verify that a X/Y error bar property is supported for the chart type.
* All chart types, except Bar have Y error bars. Only Bar and Scatter
* support X error bars.
*/
Expand Down
12 changes: 11 additions & 1 deletion src/utility.c
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ char *error_strings[LXW_MAX_ERRNO + 1] = {
"Feature is not currently supported in this configuration.",
"NULL function parameter ignored.",
"Function parameter validation error.",
"Worksheet name cannot be blank.",
"Function string parameter is empty.",
"Worksheet name exceeds Excel's limit of 31 characters.",
"Worksheet name cannot contain invalid characters: '[ ] : * ? / \\'",
"Worksheet name cannot start or end with an apostrophe.",
Expand Down Expand Up @@ -516,6 +516,16 @@ lxw_str_tolower(char *str)
str[i] = tolower(str[i]);
}

/* Simple check for empty strings. */
uint8_t
lxw_str_is_empty(const char *str)
{
if (str[0] == '\0')
return 1;
else
return 0;
}

/* Create a quoted version of the worksheet name, or return an unmodified
* copy if it doesn't required quoting. */
char *
Expand Down
54 changes: 45 additions & 9 deletions src/workbook.c
Original file line number Diff line number Diff line change
Expand Up @@ -678,8 +678,8 @@ _store_defined_name(lxw_workbook *self, const char *name,
if (!name || !formula)
return LXW_ERROR_NULL_PARAMETER_IGNORED;

if (strlen(name) == 0 || strlen(formula) == 0)
return LXW_ERROR_PARAMETER_VALIDATION;
if (lxw_str_is_empty(name) || lxw_str_is_empty(formula))
return LXW_ERROR_PARAMETER_IS_EMPTY;

if (lxw_utf8_strlen(name) > LXW_DEFINED_NAME_LENGTH ||
lxw_utf8_strlen(formula) > LXW_DEFINED_NAME_LENGTH) {
Expand Down Expand Up @@ -713,7 +713,7 @@ _store_defined_name(lxw_workbook *self, const char *name,
tmp_str++;
worksheet_name = name_copy;

if (strlen(tmp_str) == 0 || strlen(worksheet_name) == 0)
if (lxw_str_is_empty(tmp_str) || lxw_str_is_empty(worksheet_name))
goto mem_error;

/* Remove any worksheet quoting. */
Expand Down Expand Up @@ -939,8 +939,8 @@ _populate_range_dimensions(lxw_workbook *self, lxw_series_range *range)
return;
}
else {
/* Peek forward to check for empty string. */
if (tmp_str[1] == '\0') {
/* Check for empty string. */
if (lxw_str_is_empty(tmp_str)) {
range->ignore_cache = LXW_TRUE;
return;
}
Expand All @@ -950,7 +950,7 @@ _populate_range_dimensions(lxw_workbook *self, lxw_series_range *range)
tmp_str++;
sheetname = formula;

if (strlen(tmp_str) == 0 || strlen(sheetname) == 0) {
if (lxw_str_is_empty(tmp_str) || lxw_str_is_empty(sheetname)) {
range->ignore_cache = LXW_TRUE;
return;
}
Expand Down Expand Up @@ -2374,6 +2374,12 @@ workbook_set_custom_property_string(lxw_workbook *self, const char *name,
return LXW_ERROR_NULL_PARAMETER_IGNORED;
}

if (lxw_str_is_empty(name)) {
LXW_WARN_FORMAT("workbook_set_custom_property_string(): "
"parameter 'name' cannot be an empty string.");
return LXW_ERROR_PARAMETER_IS_EMPTY;
}

if (!value) {
LXW_WARN_FORMAT("workbook_set_custom_property_string(): "
"parameter 'value' cannot be NULL.");
Expand Down Expand Up @@ -2421,6 +2427,12 @@ workbook_set_custom_property_number(lxw_workbook *self, const char *name,
return LXW_ERROR_NULL_PARAMETER_IGNORED;
}

if (lxw_str_is_empty(name)) {
LXW_WARN_FORMAT("workbook_set_custom_property_number(): parameter "
"'name' cannot be an empty string.");
return LXW_ERROR_PARAMETER_IS_EMPTY;
}

if (lxw_utf8_strlen(name) > 255) {
LXW_WARN_FORMAT("workbook_set_custom_property_number(): parameter "
"'name' exceeds Excel length limit of 255.");
Expand Down Expand Up @@ -2456,6 +2468,12 @@ workbook_set_custom_property_integer(lxw_workbook *self, const char *name,
return LXW_ERROR_NULL_PARAMETER_IGNORED;
}

if (lxw_str_is_empty(name)) {
LXW_WARN_FORMAT("workbook_set_custom_property_integer(): parameter "
"'name' cannot be an empty string.");
return LXW_ERROR_PARAMETER_IS_EMPTY;
}

if (strlen(name) > 255) {
LXW_WARN_FORMAT("workbook_set_custom_property_integer(): parameter "
"'name' exceeds Excel length limit of 255.");
Expand Down Expand Up @@ -2491,6 +2509,12 @@ workbook_set_custom_property_boolean(lxw_workbook *self, const char *name,
return LXW_ERROR_NULL_PARAMETER_IGNORED;
}

if (lxw_str_is_empty(name)) {
LXW_WARN_FORMAT("workbook_set_custom_property_boolean(): parameter "
"'name' cannot be an empty string.");
return LXW_ERROR_PARAMETER_IS_EMPTY;
}

if (lxw_utf8_strlen(name) > 255) {
LXW_WARN_FORMAT("workbook_set_custom_property_boolean(): parameter "
"'name' exceeds Excel length limit of 255.");
Expand Down Expand Up @@ -2526,6 +2550,12 @@ workbook_set_custom_property_datetime(lxw_workbook *self, const char *name,
return LXW_ERROR_NULL_PARAMETER_IGNORED;
}

if (lxw_str_is_empty(name)) {
LXW_WARN_FORMAT("workbook_set_custom_property_datetime(): parameter "
"'name' cannot be an empty string.");
return LXW_ERROR_PARAMETER_IS_EMPTY;
}

if (lxw_utf8_strlen(name) > 255) {
LXW_WARN_FORMAT("workbook_set_custom_property_datetime(): parameter "
"'name' exceeds Excel length limit of 255.");
Expand Down Expand Up @@ -2627,9 +2657,9 @@ workbook_validate_sheet_name(lxw_workbook *self, const char *sheetname)
if (sheetname == NULL)
return LXW_ERROR_NULL_PARAMETER_IGNORED;

/* Check for blank worksheet name. */
if (strlen(sheetname) == 0)
return LXW_ERROR_SHEETNAME_IS_BLANK;
/* Check for empty worksheet name. */
if (lxw_str_is_empty(sheetname))
return LXW_ERROR_PARAMETER_IS_EMPTY;

/* Check the UTF-8 length of the worksheet name. */
if (lxw_utf8_strlen(sheetname) > LXW_SHEETNAME_MAX)
Expand Down Expand Up @@ -2729,6 +2759,12 @@ workbook_set_vba_name(lxw_workbook *self, const char *name)
return LXW_ERROR_NULL_PARAMETER_IGNORED;
}

if (lxw_str_is_empty(name)) {
LXW_WARN_FORMAT("workbook_set_vba_name(): parameter "
"'name' cannot be an empty string.");
return LXW_ERROR_PARAMETER_IS_EMPTY;
}

self->vba_codename = lxw_strdup(name);

return LXW_NO_ERROR;
Expand Down
12 changes: 12 additions & 0 deletions src/worksheet.c
Original file line number Diff line number Diff line change
Expand Up @@ -7944,6 +7944,9 @@ worksheet_write_formula_num(lxw_worksheet *self,
if (!formula)
return LXW_ERROR_NULL_PARAMETER_IGNORED;

if (lxw_str_is_empty(formula))
return LXW_ERROR_PARAMETER_IS_EMPTY;

err = _check_dimensions(self, row_num, col_num, LXW_FALSE, LXW_FALSE);
if (err)
return err;
Expand Down Expand Up @@ -7979,6 +7982,9 @@ worksheet_write_formula_str(lxw_worksheet *self,
if (!formula)
return LXW_ERROR_NULL_PARAMETER_IGNORED;

if (lxw_str_is_empty(formula))
return LXW_ERROR_PARAMETER_IS_EMPTY;

err = _check_dimensions(self, row_num, col_num, LXW_FALSE, LXW_FALSE);
if (err)
return err;
Expand Down Expand Up @@ -8044,6 +8050,9 @@ _store_array_formula(lxw_worksheet *self,
if (!formula)
return LXW_ERROR_NULL_PARAMETER_IGNORED;

if (lxw_str_is_empty(formula))
return LXW_ERROR_PARAMETER_IS_EMPTY;

/* Check that row and col are valid and store max and min values. */
err = _check_dimensions(self, first_row, first_col, LXW_FALSE, LXW_FALSE);
if (err)
Expand Down Expand Up @@ -8670,6 +8679,9 @@ worksheet_write_comment_opt(lxw_worksheet *self,
if (!text)
return LXW_ERROR_NULL_PARAMETER_IGNORED;

if (lxw_str_is_empty(text))
return LXW_ERROR_PARAMETER_IS_EMPTY;

if (lxw_utf8_strlen(text) > LXW_STR_MAX)
return LXW_ERROR_MAX_STRING_LENGTH_EXCEEDED;

Expand Down
4 changes: 2 additions & 2 deletions test/unit/workbook/test_workbook_validate_worksheet_name.c
Original file line number Diff line number Diff line change
Expand Up @@ -147,13 +147,13 @@ CTEST(workbook, validate_worksheet_name09) {
lxw_workbook_free(workbook);
}

/* Test for blank sheet name. */
/* Test for empty sheet name. */
CTEST(workbook, validate_worksheet_name10) {

const char* sheetname = "";

lxw_workbook *workbook = workbook_new(NULL);
lxw_error exp = LXW_ERROR_SHEETNAME_IS_BLANK;
lxw_error exp = LXW_ERROR_PARAMETER_IS_EMPTY;
lxw_error got = workbook_validate_sheet_name(workbook, sheetname);

ASSERT_EQUAL(exp, got);
Expand Down

0 comments on commit c89c551

Please sign in to comment.