CVE-2021-34121 (published 2023-07-18T14:15Z)

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.

CVSS v3 base score: 7.8, HIGH (CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H)

Weakness information:
cwe_id: CWE-125
cwe_name: Out-of-bounds Read
description: The product reads data past the end, or before the beginning, of the intended buffer.
extended_description: 
            
            
             
			 
         
         

Fix information:
repo_url: https://github.com/michaelrsweet/htmldoc
commit_details: repo_url: https://github.com/michaelrsweet/htmldoc
author: Michael R Sweet
author_date: 2021-05-27 08:58:21-04:00
msg: Fix array overflow for headings using roman numerals (Issue #433)
repository: repo_url: https://github.com/michaelrsweet/htmldoc
repo_name: michaelrsweet/htmldoc
description: HTML Conversion Software
repo_language: C++
owner: michaelrsweet
file_changes: filename: CHANGES.md
change_type: ModificationType.MODIFY
diff: @@ -1,3 +1,9 @@
+# Changes in HTMLDOC v1.9.13
+
+- Fixed an issue with large values for roman numerals and letters in headings
+  (Issue #433)
+
+
 # Changes in HTMLDOC v1.9.12
 
 - Fixed a crash bug with "data:" URIs and EPUB output (Issue #410)

programming_language: Markdown
filename: toc.cxx
change_type: ModificationType.MODIFY
diff: @@ -122,36 +122,6 @@ parse_tree(tree_t *t)		/* I - Document tree */
 		*existing;	/* Existing link string */
   int		i, level;	/* Header level */
   uchar		*var;		/* Starting value/type for this level */
-  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"
-		};
 
 
   while (t != NULL)
@@ -212,30 +182,7 @@ parse_tree(tree_t *t)		/* I - Document tree */
             else
               snprintf((char *)baseptr, sizeof(baselink) - (size_t)(baseptr - baselink), "%d", heading_numbers[i]);
 
-            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;
-            }
+            strlcpy((char *)headptr, format_number(heading_numbers[i], heading_types[i]), sizeof(heading) - (size_t)(headptr - heading));
 
             if (i < level)
             {

programming_language: C
filename: util.cxx
change_type: ModificationType.MODIFY
diff: @@ -35,10 +35,14 @@ format_number(int  n,		/* I - Number */
 		  "",	"x",	"xx",	"xxx",	"xl",
 		  "l",	"lx",	"lxx",	"lxxx",	"xc"
 		},
-		*hundreds[10] =	/* Roman numerals, 100-900 */
+		*hundreds[30] =	/* Roman numerals, 100-2900 */
 		{
-		  "",	"c",	"cc",	"ccc",	"cd",
-		  "d",	"dc",	"dcc",	"dccc",	"cm"
+		  "",    "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"
 		};
   static const char *ONES[10] =	/* Roman numerals, 0-9 */
 		{
@@ -50,10 +54,14 @@ format_number(int  n,		/* I - Number */
 		  "",	"X",	"XX",	"XXX",	"XL",
 		  "L",	"LX",	"LXX",	"LXXX",	"XC"
 		},
-		*HUNDREDS[10] =	/* Roman numerals, 100-900 */
+		*HUNDREDS[30] =	/* Roman numerals, 100-2900 */
 		{
-		  "",	"C",	"CC",	"CCC",	"CD",
-		  "D",	"DC",	"DCC",	"DCCC",	"CM"
+		  "",    "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"
 		};
   static char	buffer[1024];	/* String buffer */
 
@@ -65,18 +73,20 @@ format_number(int  n,		/* I - Number */
 	break;
 
     case 'a' :
-        if (n >= (26 * 26))
-	  buffer[0] = '\0';
-        else if (n > 26)
+        if (n > (26 * 26))
+          n = (n % (26 * 26)) + 1;
+
+        if (n > 26)
           snprintf(buffer, sizeof(buffer), "%c%c", 'a' + (n / 26) - 1, 'a' + (n % 26) - 1);
         else
           snprintf(buffer, sizeof(buffer), "%c", 'a' + n - 1);
         break;
 
     case 'A' :
-        if (n >= (26 * 26))
-	  buffer[0] = '\0';
-        else if (n > 26)
+        if (n > (26 * 26))
+          n = (n % (26 * 26)) + 1;
+
+        if (n > 26)
           snprintf(buffer, sizeof(buffer), "%c%c", 'A' + (n / 26) - 1, 'A' + (n % 26) - 1);
         else
           snprintf(buffer, sizeof(buffer), "%c", 'A' + n - 1);
@@ -87,17 +97,17 @@ format_number(int  n,		/* I - Number */
         break;
 
     case 'i' :
-        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 >= 3000)
+          n = (n % 3000) + 1;
+        
+	snprintf(buffer, sizeof(buffer), "%s%s%s", hundreds[n / 100], tens[(n / 10) % 10], ones[n % 10]);
         break;
 
     case 'I' :
-        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 >= 3000)
+          n = (n % 3000) + 1;
+        
+	snprintf(buffer, sizeof(buffer), "%s%s%s", HUNDREDS[n / 100], TENS[(n / 10) % 10], ONES[n % 10]);
         break;
   }
 

programming_language: C