CVE-2021-34121 (Other, published 2023-07-18T14:15Z)
Description: value: An Out of Bounds flaw was discovered in htmodoc 1.9.12 in function parse_tree() in toc.cxx, this possibly leads to memory layout information leaking in the data. This might be used in a chain of vulnerability in order to reach code execution.
Severity: nan (CVSS v3 base score: 7.8)
Weakness: CWE-125 \u2014 Out-of-bounds Read
The product reads data past the end, or before the beginning, of the intended buffer.
Repository: https://github.com/michaelrsweet/htmldoc
Files changed:
CHANGES.md
htmldoc/toc.cxx
htmldoc/util.cxx
Commit message: Fix array overflow for headings using roman numerals (Issue #433)


Vulnerable code:
  static const char *ones[10] =
		{
		  "",	"i",	"ii",	"iii",	"iv",
		  "v",	"vi",	"vii",	"viii",	"ix"
		},
		*tens[10] =
		{
		  "",	"x",	"xx",	"xxx",	"xl",
		  "l",	"lx",	"lxx",	"lxxx",	"xc"
		},
		*hundreds[10] =
		{
		  "",	"c",	"cc",	"ccc",	"cd",
		  "d",	"dc",	"dcc",	"dccc",	"cm"
		},
		*ONES[10] =
		{
		  "",	"I",	"II",	"III",	"IV",
		  "V",	"VI",	"VII",	"VIII",	"IX"
		},
		*TENS[10] =
		{
		  "",	"X",	"XX",	"XXX",	"XL",
		  "L",	"LX",	"LXX",	"LXXX",	"XC"
		},
		*HUNDREDS[10] =
		{
		  "",	"C",	"CC",	"CCC",	"CD",
		  "D",	"DC",	"DCC",	"DCCC",	"CM"
		};
            switch (heading_types[i])
            {
              case '1' :
                  snprintf((char *)headptr, sizeof(heading) - (size_t)(headptr - heading), "%d", heading_numbers[i]);
                  break;
              case 'a' :
                  if (heading_numbers[i] > 26)
                    snprintf((char *)headptr, sizeof(heading) - (size_t)(headptr - heading), "%c%c", 'a' + (heading_numbers[i] / 26) - 1, 'a' + (heading_numbers[i] % 26) - 1);
                  else
                    snprintf((char *)headptr, sizeof(heading) - (size_t)(headptr - heading), "%c", 'a' + heading_numbers[i] - 1);
                  break;
              case 'A' :
                  if (heading_numbers[i] > 26)
                    snprintf((char *)headptr, sizeof(heading) - (size_t)(headptr - heading), "%c%c", 'A' + (heading_numbers[i] / 26) - 1, 'A' + (heading_numbers[i] % 26) - 1);
                  else
                    snprintf((char *)headptr, sizeof(heading) - (size_t)(headptr - heading), "%c", 'A' + heading_numbers[i] - 1);
                  break;
              case 'i' :
                  snprintf((char *)headptr, sizeof(heading) - (size_t)(headptr - heading), "%s%s%s", hundreds[heading_numbers[i] / 100], tens[(heading_numbers[i] / 10) % 10], ones[heading_numbers[i] % 10]);
                  break;
              case 'I' :
                  snprintf((char *)headptr, sizeof(heading) - (size_t)(headptr - heading), "%s%s%s", HUNDREDS[heading_numbers[i] / 100], TENS[(heading_numbers[i] / 10) % 10], ONES[heading_numbers[i] % 10]);
                  break;
            }
		*hundreds[10] =	/* Roman numerals, 100-900 */
		  "",	"c",	"cc",	"ccc",	"cd",
		  "d",	"dc",	"dcc",	"dccc",	"cm"
		*HUNDREDS[10] =	/* Roman numerals, 100-900 */
		  "",	"C",	"CC",	"CCC",	"CD",
		  "D",	"DC",	"DCC",	"DCCC",	"CM"
        if (n >= (26 * 26))
	  buffer[0] = '\0';
        else if (n > 26)
        if (n >= (26 * 26))
	  buffer[0] = '\0';
        else if (n > 26)
        if (n >= 1000)
	  buffer[0] = '\0';
	else
          snprintf(buffer, sizeof(buffer), "%s%s%s", hundreds[n / 100], tens[(n / 10) % 10], ones[n % 10]);
        if (n >= 1000)
	  buffer[0] = '\0';
	else
          snprintf(buffer, sizeof(buffer), "%s%s%s", HUNDREDS[n / 100], TENS[(n / 10) % 10], ONES[n % 10]);

Fixed code:
# Changes in HTMLDOC v1.9.13

- Fixed an issue with large values for roman numerals and letters in headings
  (Issue #433)


            strlcpy((char *)headptr, format_number(heading_numbers[i], heading_types[i]), sizeof(heading) - (size_t)(headptr - heading));
		*hundreds[30] =	/* Roman numerals, 100-2900 */
		  "",    "c",    "cc",    "ccc",    "cd",
		  "d",   "dc",   "dcc",   "dccc",   "cm",
		  "m",   "mc",   "cc",    "ccc",    "cd",
		  "m",   "mdc",  "mdcc",  "mdccc",  "mcm",
		  "mm",  "mmc",  "mmcc",  "mmccc",  "mmcd",
		  "mmd", "mmdc", "mmdcc", "mmdccc", "mmcm"
		*HUNDREDS[30] =	/* Roman numerals, 100-2900 */
		  "",    "C",    "CC",    "CCC",    "CD",
		  "D",   "DC",   "DCC",   "DCCC",   "CM",
		  "M",   "MC",   "CC",    "CCC",    "CD",
		  "M",   "MDC",  "MDCC",  "MDCCC",  "MCM",
		  "MM",  "MMC",  "MMCC",  "MMCCC",  "MMCD",
		  "MMD", "MMDC", "MMDCC", "MMDCCC", "MMCM"
        if (n > (26 * 26))
          n = (n % (26 * 26)) + 1;

        if (n > 26)
        if (n > (26 * 26))
          n = (n % (26 * 26)) + 1;

        if (n > 26)
        if (n >= 3000)
          n = (n % 3000) + 1;
        
	snprintf(buffer, sizeof(buffer), "%s%s%s", hundreds[n / 100], tens[(n / 10) % 10], ones[n % 10]);
        if (n >= 3000)
          n = (n % 3000) + 1;
        
	snprintf(buffer, sizeof(buffer), "%s%s%s", HUNDREDS[n / 100], TENS[(n / 10) % 10], ONES[n % 10]);