Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

src/cfgparse.c: use after free suspected by coverity #1563

Closed
chipitsine opened this issue Feb 21, 2022 · 10 comments
Closed

src/cfgparse.c: use after free suspected by coverity #1563

chipitsine opened this issue Feb 21, 2022 · 10 comments
Labels
status: fixed This issue is a now-fixed bug. type: code-report This issue describes a code report (like valgrind or coverity)

Comments

@chipitsine
Copy link
Member

Tool Name and Version

coverity

Code Report

1685int readcfgfile(const char *file)
1686{
1687        char *thisline = NULL;
1688        int linesize = LINESIZE;
1689        FILE *f = NULL;
1690        int linenum = 0;
1691        int err_code = 0;
1692        struct cfg_section *cs = NULL, *pcs = NULL;
1693        struct cfg_section *ics;
1694        int readbytes = 0;
1695        char *outline = NULL;
1696        size_t outlen = 0;
1697        size_t outlinesize = 0;
1698        int fatal = 0;
1699        int missing_lf = -1;
1700        int nested_cond_lvl = 0;
1701        enum nested_cond_state nested_conds[MAXNESTEDCONDS];
1702        int non_global_section_parsed = 0;
1703        char *errmsg = NULL;
1704
1705        global.cfg_curr_line = 0;
1706        global.cfg_curr_file = file;
1707
    	1. Condition (thisline = malloc(1UL /* sizeof (*thisline) */ * linesize)) == NULL, taking false branch.
1708        if ((thisline = malloc(sizeof(*thisline) * linesize)) == NULL) {
1709                ha_alert("Out of memory trying to allocate a buffer for a configuration line.\n");
1710                err_code = -1;
1711                goto err;
1712        }
1713
    	2. Condition (f = fopen(file, "r")) == NULL, taking false branch.
1714        if ((f = fopen(file,"r")) == NULL) {
1715                err_code = -1;
1716                goto err;
1717        }
1718
1719        /* change to the new dir if required */
    	3. Condition !cfg_apply_default_path(file, NULL, &errmsg), taking false branch.
1720        if (!cfg_apply_default_path(file, NULL, &errmsg)) {
1721                ha_alert("parsing [%s:%d]: failed to apply default-path: %s.\n", file, linenum, errmsg);
1722                free(errmsg);
1723                err_code = -1;
1724                goto err;
1725        }
1726
1727next_line:
    	4. Condition fgets(thisline + readbytes, linesize - readbytes, f) != NULL, taking true branch.
    	11. Condition fgets(thisline + readbytes, linesize - readbytes, f) != NULL, taking true branch.
    	18. Condition fgets(thisline + readbytes, linesize - readbytes, f) != NULL, taking true branch.
    	25. Condition fgets(thisline + readbytes, linesize - readbytes, f) != NULL, taking true branch.
    	32. Condition fgets(thisline + readbytes, linesize - readbytes, f) != NULL, taking true branch.
    	45. Condition fgets(thisline + readbytes, linesize - readbytes, f) != NULL, taking true branch.
    	52. Condition fgets(thisline + readbytes, linesize - readbytes, f) != NULL, taking true branch.
    	59. Condition fgets(thisline + readbytes, linesize - readbytes, f) != NULL, taking true branch.
    	72. Condition fgets(thisline + readbytes, linesize - readbytes, f) != NULL, taking true branch.
    	88. Condition fgets(thisline + readbytes, linesize - readbytes, f) != NULL, taking true branch.
    	101. Condition fgets(thisline + readbytes, linesize - readbytes, f) != NULL, taking true branch.
    	108. Condition fgets(thisline + readbytes, linesize - readbytes, f) != NULL, taking true branch.
    	135. Condition fgets(thisline + readbytes, linesize - readbytes, f) != NULL, taking true branch.
    	165. Condition fgets(thisline + readbytes, linesize - readbytes, f) != NULL, taking true branch.
    	197. Condition fgets(thisline + readbytes, linesize - readbytes, f) != NULL, taking true branch.
1728        while (fgets(thisline + readbytes, linesize - readbytes, f) != NULL) {
1729                int arg, kwm = KWM_STD;
1730                char *end;
1731                char *args[MAX_LINE_ARGS + 1];
1732                char *line = thisline;
1733
    	5. Condition missing_lf != -1, taking false branch.
    	12. Condition missing_lf != -1, taking false branch.
    	19. Condition missing_lf != -1, taking false branch.
    	26. Condition missing_lf != -1, taking false branch.
    	33. Condition missing_lf != -1, taking false branch.
    	46. Condition missing_lf != -1, taking false branch.
    	53. Condition missing_lf != -1, taking false branch.
    	60. Condition missing_lf != -1, taking false branch.
    	73. Condition missing_lf != -1, taking false branch.
    	89. Condition missing_lf != -1, taking false branch.
    	102. Condition missing_lf != -1, taking false branch.
    	109. Condition missing_lf != -1, taking false branch.
    	136. Condition missing_lf != -1, taking false branch.
    	166. Condition missing_lf != -1, taking false branch.
    	198. Condition missing_lf != -1, taking false branch.
1734                if (missing_lf != -1) {
1735                        ha_alert("parsing [%s:%d]: Stray NUL character at position %d.\n",
1736                                 file, linenum, (missing_lf + 1));
1737                        err_code |= ERR_ALERT | ERR_FATAL;
1738                        missing_lf = -1;
1739                        break;
1740                }
1741
1742                linenum++;
1743                global.cfg_curr_line = linenum;
1744
    	6. Condition fatal >= 50, taking false branch.
    	13. Condition fatal >= 50, taking false branch.
    	20. Condition fatal >= 50, taking false branch.
    	27. Condition fatal >= 50, taking false branch.
    	34. Condition fatal >= 50, taking false branch.
    	47. Condition fatal >= 50, taking false branch.
    	54. Condition fatal >= 50, taking false branch.
    	61. Condition fatal >= 50, taking false branch.
    	74. Condition fatal >= 50, taking false branch.
    	90. Condition fatal >= 50, taking false branch.
    	103. Condition fatal >= 50, taking false branch.
    	110. Condition fatal >= 50, taking false branch.
    	137. Condition fatal >= 50, taking false branch.
    	167. Condition fatal >= 50, taking false branch.
    	199. Condition fatal >= 50, taking false branch.
1745                if (fatal >= 50) {
1746                        ha_alert("parsing [%s:%d]: too many fatal errors (%d), stopping now.\n", file, linenum, fatal);
1747                        break;
1748                }
1749
1750                end = line + strlen(line);
1751
    	7. Condition end - line == linesize - 1, taking true branch.
    	8. Condition *(end - 1) != 10, taking true branch.
    	14. Condition end - line == linesize - 1, taking true branch.
    	15. Condition *(end - 1) != 10, taking true branch.
    	21. Condition end - line == linesize - 1, taking true branch.
    	22. Condition *(end - 1) != 10, taking true branch.
    	28. Condition end - line == linesize - 1, taking true branch.
    	29. Condition *(end - 1) != 10, taking true branch.
    	35. Condition end - line == linesize - 1, taking true branch.
    	36. Condition *(end - 1) != 10, taking false branch.
    	48. Condition end - line == linesize - 1, taking true branch.
    	49. Condition *(end - 1) != 10, taking true branch.
    	55. Condition end - line == linesize - 1, taking true branch.
    	56. Condition *(end - 1) != 10, taking true branch.
    	62. Condition end - line == linesize - 1, taking true branch.
    	63. Condition *(end - 1) != 10, taking false branch.
    	75. Condition end - line == linesize - 1, taking true branch.
    	76. Condition *(end - 1) != 10, taking false branch.
    	91. Condition end - line == linesize - 1, taking true branch.
    	92. Condition *(end - 1) != 10, taking false branch.
    	104. Condition end - line == linesize - 1, taking true branch.
    	105. Condition *(end - 1) != 10, taking true branch.
    	111. Condition end - line == linesize - 1, taking true branch.
    	112. Condition *(end - 1) != 10, taking false branch.
    	138. Condition end - line == linesize - 1, taking true branch.
    	139. Condition *(end - 1) != 10, taking false branch.
    	168. Condition end - line == linesize - 1, taking true branch.
    	169. Condition *(end - 1) != 10, taking false branch.
    	200. Condition end - line == linesize - 1, taking true branch.
    	201. Condition *(end - 1) != 10, taking false branch.
1752                if (end-line == linesize-1 && *(end-1) != '\n') {
1753                        /* Check if we reached the limit and the last char is not \n.
1754                         * Watch out for the last line without the terminating '\n'!
1755                         */
1756                        char *newline;
1757                        int newlinesize = linesize * 2;
1758
1759                        newline = realloc(thisline, sizeof(*thisline) * newlinesize);
    	9. Condition newline == NULL, taking false branch.
    	16. Condition newline == NULL, taking false branch.
    	23. Condition newline == NULL, taking true branch.
    	30. Condition newline == NULL, taking true branch.
    	50. Condition newline == NULL, taking false branch.
    	57. Condition newline == NULL, taking false branch.
    	106. Condition newline == NULL, taking false branch.
1760                        if (newline == NULL) {
1761                                ha_alert("parsing [%s:%d]: line too long, cannot allocate memory.\n",
1762                                         file, linenum);
1763                                err_code |= ERR_ALERT | ERR_FATAL;
1764                                fatal++;
1765                                linenum--;
    	24. Continuing loop.
    	31. Continuing loop.
1766                                continue;
1767                        }
1768
1769                        readbytes = linesize - 1;
1770                        linesize = newlinesize;
1771                        thisline = newline;
1772                        linenum--;
    	10. Continuing loop.
    	17. Continuing loop.
    	51. Continuing loop.
    	58. Continuing loop.
    	107. Continuing loop.
1773                        continue;
1774                }
1775
1776                readbytes = 0;
1777
    	37. Condition end > line, taking true branch.
    	38. Condition *(end - 1) == 10, taking true branch.
    	64. Condition end > line, taking true branch.
    	65. Condition *(end - 1) == 10, taking true branch.
    	77. Condition end > line, taking true branch.
    	78. Condition *(end - 1) == 10, taking true branch.
    	93. Condition end > line, taking true branch.
    	94. Condition *(end - 1) == 10, taking true branch.
    	113. Condition end > line, taking true branch.
    	114. Condition *(end - 1) == 10, taking true branch.
    	140. Condition end > line, taking true branch.
    	141. Condition *(end - 1) == 10, taking true branch.
    	170. Condition end > line, taking true branch.
    	171. Condition *(end - 1) == 10, taking true branch.
    	202. Condition end > line, taking true branch.
    	203. Condition *(end - 1) == 10, taking true branch.
1778                if (end > line && *(end-1) == '\n') {
1779                        /* kill trailing LF */
1780                        *(end - 1) = 0;
    	39. Falling through to end of if statement.
    	66. Falling through to end of if statement.
    	79. Falling through to end of if statement.
    	95. Falling through to end of if statement.
    	115. Falling through to end of if statement.
    	142. Falling through to end of if statement.
    	172. Falling through to end of if statement.
    	204. Falling through to end of if statement.
1781                }
1782                else {
1783                        /* mark this line as truncated */
1784                        missing_lf = end - line;
1785                }
1786
1787                /* skip leading spaces */
    	40. Condition *__ctype_b_loc()[(int)(unsigned char)*line] & 8192 /* (unsigned short)_ISspace */, taking true branch.
    	42. Condition *__ctype_b_loc()[(int)(unsigned char)*line] & 8192 /* (unsigned short)_ISspace */, taking false branch.
    	67. Condition *__ctype_b_loc()[(int)(unsigned char)*line] & 8192 /* (unsigned short)_ISspace */, taking true branch.
    	69. Condition *__ctype_b_loc()[(int)(unsigned char)*line] & 8192 /* (unsigned short)_ISspace */, taking false branch.
    	80. Condition *__ctype_b_loc()[(int)(unsigned char)*line] & 8192 /* (unsigned short)_ISspace */, taking false branch.
    	96. Condition *__ctype_b_loc()[(int)(unsigned char)*line] & 8192 /* (unsigned short)_ISspace */, taking true branch.
    	98. Condition *__ctype_b_loc()[(int)(unsigned char)*line] & 8192 /* (unsigned short)_ISspace */, taking false branch.
    	116. Condition *__ctype_b_loc()[(int)(unsigned char)*line] & 8192 /* (unsigned short)_ISspace */, taking true branch.
    	118. Condition *__ctype_b_loc()[(int)(unsigned char)*line] & 8192 /* (unsigned short)_ISspace */, taking false branch.
    	143. Condition *__ctype_b_loc()[(int)(unsigned char)*line] & 8192 /* (unsigned short)_ISspace */, taking true branch.
    	145. Condition *__ctype_b_loc()[(int)(unsigned char)*line] & 8192 /* (unsigned short)_ISspace */, taking false branch.
    	173. Condition *__ctype_b_loc()[(int)(unsigned char)*line] & 8192 /* (unsigned short)_ISspace */, taking true branch.
    	175. Condition *__ctype_b_loc()[(int)(unsigned char)*line] & 8192 /* (unsigned short)_ISspace */, taking false branch.
    	205. Condition *__ctype_b_loc()[(int)(unsigned char)*line] & 8192 /* (unsigned short)_ISspace */, taking true branch.
    	207. Condition *__ctype_b_loc()[(int)(unsigned char)*line] & 8192 /* (unsigned short)_ISspace */, taking false branch.
1788                while (isspace((unsigned char)*line))
    	41. Jumping back to the beginning of the loop.
    	68. Jumping back to the beginning of the loop.
    	97. Jumping back to the beginning of the loop.
    	117. Jumping back to the beginning of the loop.
    	144. Jumping back to the beginning of the loop.
    	174. Jumping back to the beginning of the loop.
    	206. Jumping back to the beginning of the loop.
1789                        line++;
1790
    	43. Condition *line == '[', taking true branch.
    	70. Condition *line == '[', taking true branch.
    	81. Condition *line == '[', taking false branch.
    	99. Condition *line == '[', taking true branch.
    	119. Condition *line == '[', taking false branch.
    	146. Condition *line == '[', taking false branch.
    	176. Condition *line == '[', taking false branch.
    	208. Condition *line == '[', taking false branch.
1791                if (*line == '[') {/* This is the beginning if a scope */
1792                        err_code |= cfg_parse_scope(file, linenum, line);
    	44. Jumping to label next_line.
    	71. Jumping to label next_line.
    	100. Jumping to label next_line.
1793                        goto next_line;
1794                }
1795
    	82. Condition 1, taking true branch.
    	120. Condition 1, taking true branch.
    	129. Condition 1, taking true branch.
    	147. Condition 1, taking true branch.
    	156. Condition 1, taking true branch.
    	177. Condition 1, taking true branch.
    	186. Condition 1, taking true branch.
    	209. Condition 1, taking true branch.
1796                while (1) {
1797                        uint32_t err;
1798                        const char *errptr;
1799
1800                        arg = sizeof(args) / sizeof(*args);
1801                        outlen = outlinesize;
      	CID 1429926 (2): Dereference after null check (FORWARD_NULL) [select issue]
    	187. identity_transfer: Passing outline as argument 2 to function parse_line, which sets *args to that argument. [show details]
1802                        err = parse_line(line, outline, &outlen, args, &arg,
1803                                         PARSE_OPT_ENV | PARSE_OPT_DQUOTE | PARSE_OPT_SQUOTE |
1804                                         PARSE_OPT_BKSLASH | PARSE_OPT_SHARP | PARSE_OPT_WORD_EXPAND,
1805                                         &errptr);
1806
    	83. Condition err & 4, taking false branch.
    	121. Condition err & 4, taking false branch.
    	130. Condition err & 4, taking false branch.
    	148. Condition err & 4, taking false branch.
    	157. Condition err & 4, taking false branch.
    	178. Condition err & 4, taking false branch.
    	188. Condition err & 4, taking false branch.
    	210. Condition err & 4, taking false branch.
1807                        if (err & PARSE_ERR_QUOTE) {
1808                                size_t newpos = sanitize_for_printing(line, errptr - line, 80);
1809
1810                                ha_alert("parsing [%s:%d]: unmatched quote at position %d:\n"
1811                                         "  %s\n  %*s\n", file, linenum, (int)(errptr-thisline+1), line, (int)(newpos+1), "^");
1812                                err_code |= ERR_ALERT | ERR_FATAL;
1813                                fatal++;
1814                                goto next_line;
1815                        }
1816
    	84. Condition err & 8, taking false branch.
    	122. Condition err & 8, taking false branch.
    	131. Condition err & 8, taking false branch.
    	149. Condition err & 8, taking false branch.
    	158. Condition err & 8, taking false branch.
    	179. Condition err & 8, taking false branch.
    	189. Condition err & 8, taking false branch.
    	211. Condition err & 8, taking false branch.
1817                        if (err & PARSE_ERR_BRACE) {
1818                                size_t newpos = sanitize_for_printing(line, errptr - line, 80);
1819
1820                                ha_alert("parsing [%s:%d]: unmatched brace in environment variable name at position %d:\n"
1821                                         "  %s\n  %*s\n", file, linenum, (int)(errptr-thisline+1), line, (int)(newpos+1), "^");
1822                                err_code |= ERR_ALERT | ERR_FATAL;
1823                                fatal++;
1824                                goto next_line;
1825                        }
1826
    	85. Condition err & 32, taking false branch.
    	123. Condition err & 32, taking false branch.
    	132. Condition err & 32, taking false branch.
    	150. Condition err & 32, taking false branch.
    	159. Condition err & 32, taking false branch.
    	180. Condition err & 32, taking false branch.
    	190. Condition err & 32, taking false branch.
    	212. Condition err & 32, taking false branch.
1827                        if (err & PARSE_ERR_VARNAME) {
1828                                size_t newpos = sanitize_for_printing(line, errptr - line, 80);
1829
1830                                ha_alert("parsing [%s:%d]: forbidden first char in environment variable name at position %d:\n"
1831                                         "  %s\n  %*s\n", file, linenum, (int)(errptr-thisline+1), line, (int)(newpos+1), "^");
1832                                err_code |= ERR_ALERT | ERR_FATAL;
1833                                fatal++;
1834                                goto next_line;
1835                        }
1836
    	86. Condition err & 16, taking true branch.
    	124. Condition err & 16, taking false branch.
    	133. Condition err & 16, taking true branch.
    	151. Condition err & 16, taking false branch.
    	160. Condition err & 16, taking false branch.
    	181. Condition err & 16, taking false branch.
    	191. Condition err & 16, taking false branch.
    	213. Condition err & 16, taking false branch.
1837                        if (err & PARSE_ERR_HEX) {
1838                                size_t newpos = sanitize_for_printing(line, errptr - line, 80);
1839
1840                                ha_alert("parsing [%s:%d]: truncated or invalid hexadecimal sequence at position %d:\n"
1841                                         "  %s\n  %*s\n", file, linenum, (int)(errptr-thisline+1), line, (int)(newpos+1), "^");
1842                                err_code |= ERR_ALERT | ERR_FATAL;
1843                                fatal++;
    	87. Jumping to label next_line.
    	134. Jumping to label next_line.
1844                                goto next_line;
1845                        }
1846
    	125. Condition err & 128, taking false branch.
    	152. Condition err & 128, taking false branch.
    	161. Condition err & 128, taking false branch.
    	182. Condition err & 128, taking false branch.
    	192. Condition err & 128, taking false branch.
    	214. Condition err & 128, taking false branch.
1847                        if (err & PARSE_ERR_WRONG_EXPAND) {
1848                                size_t newpos = sanitize_for_printing(line, errptr - line, 80);
1849
1850                                ha_alert("parsing [%s:%d]: truncated or invalid word expansion sequence at position %d:\n"
1851                                         "  %s\n  %*s\n", file, linenum, (int)(errptr-thisline+1), line, (int)(newpos+1), "^");
1852                                err_code |= ERR_ALERT | ERR_FATAL;
1853                                fatal++;
1854                                goto next_line;
1855                        }
1856
    	126. Condition err & (65U /* 1 | 0x40 */), taking true branch.
    	153. Condition err & (65U /* 1 | 0x40 */), taking true branch.
    	162. Condition err & (65U /* 1 | 0x40 */), taking true branch.
    	183. Condition err & (65U /* 1 | 0x40 */), taking true branch.
    	193. Condition err & (65U /* 1 | 0x40 */), taking true branch.
    	215. Condition err & (65U /* 1 | 0x40 */), taking false branch.
1857                        if (err & (PARSE_ERR_TOOLARGE|PARSE_ERR_OVERLAP)) {
1858                                outlinesize = (outlen + 1023) & -1024;
    	194. freed_arg: my_realloc2 frees outline. [show details]
1859                                outline = my_realloc2(outline, outlinesize);
    	127. Condition outline == NULL, taking false branch.
    	154. Condition outline == NULL, taking false branch.
    	163. Condition outline == NULL, taking true branch.
    	184. Condition outline == NULL, taking false branch.
    	195. Condition outline == NULL, taking true branch.
1860                                if (outline == NULL) {
1861                                        ha_alert("parsing [%s:%d]: line too long, cannot allocate memory.\n",
1862                                                 file, linenum);
1863                                        err_code |= ERR_ALERT | ERR_FATAL;
1864                                        fatal++;
    	164. Jumping to label next_line.
    	196. Jumping to label next_line.
1865                                        goto next_line;
1866                                }
1867                                /* try again */
    	128. Continuing loop.
    	155. Continuing loop.
    	185. Continuing loop.
1868                                continue;
1869                        }
1870
    	216. Condition err & 2, taking false branch.
1871                        if (err & PARSE_ERR_TOOMANY) {
1872                                /* only check this *after* being sure the output is allocated */
1873                                ha_alert("parsing [%s:%d]: too many words, truncating after word %d, position %ld: <%s>.\n",
1874                                         file, linenum, MAX_LINE_ARGS, (long)(args[MAX_LINE_ARGS-1] - outline + 1), args[MAX_LINE_ARGS-1]);
1875                                err_code |= ERR_ALERT | ERR_FATAL;
1876                                fatal++;
1877                                goto next_line;
1878                        }
1879
1880                        /* everything's OK */
    	217. Breaking from loop.
1881                        break;
1882                }
1883
1884                /* empty line */
    	
CID 1469673 (#1 of 1): Read from pointer after free (USE_AFTER_FREE)
218. deref_after_free: Dereferencing freed pointer *args.
1885                if (!**args)
1886                        continue;
1887

Additional Information

No response

Output of haproxy -vv

no
@chipitsine chipitsine added the type: code-report This issue describes a code report (like valgrind or coverity) label Feb 21, 2022
@capflam
Copy link
Member

capflam commented May 17, 2022

Indeed, it seems there is an issue here. For me, when my_realloc2() fails, outlinesize variable must be reset to 0 :

diff --git a/src/cfgparse.c b/src/cfgparse.c
index b1ec46f2f..2f886d92e 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -1862,6 +1862,7 @@ int readcfgfile(const char *file)
                                                 file, linenum);
                                        err_code |= ERR_ALERT | ERR_FATAL;
                                        fatal++;
+                                       outlinesize = 0;
                                        goto next_line;
                                }
                                /* try again */

I replaced my_realloc2() by a free() + NULL to simulate a re-allocation error and I got a segfault in EMIT_CHAR() macro in parse_line() function. But, I'm not really comfortable with this code. @wtarreau, could you confirm it is the right fix ?

@capflam capflam added the status: reviewed This issue was reviewed. A fix is required. label May 17, 2022
haproxy-mirror pushed a commit that referenced this issue May 19, 2022
…dcfgfile()

When the line parsing failed because outline buffer must be reallocated, if
my_realloc2() call fails, the buffer size must be reset. Indeed, in this case
the current line is skipped, a fatal error is reported and we jump to the next
line. At this stage the outline buffer is NULL. If the buffer size is not reset,
the next call to parse_line() crashes because we try to write in the buffer. We
fail to detect the outline buffer is too small to copy any character.

To fix the issue, outlinesize variable must be set to 0 when outline allocation
failed.

This patch should fix the issue #1563. It must be backported as far as 2.2.
@capflam
Copy link
Member

capflam commented May 19, 2022

It should be fixed now.

@capflam capflam added status: fixed This issue is a now-fixed bug. 2.2 This issue affects the HAProxy 2.2 stable branch. 2.4 This issue affects the HAProxy 2.4 stable branch. 2.5 This issue affects the HAProxy 2.5 stable branch. and removed status: reviewed This issue was reviewed. A fix is required. labels May 19, 2022
@chipitsine
Copy link
Member Author

I'll check coverity tomorrow (after next scheduled scan).
thanks

@chipitsine
Copy link
Member Author

coverity thinks it is still unresolved

CID1469673

@capflam
Copy link
Member

capflam commented May 20, 2022

Ok, thanks. I'll review it again.

@capflam capflam added dev This issue affects the HAProxy development branch. status: reviewed This issue was reviewed. A fix is required. and removed status: fixed This issue is a now-fixed bug. labels May 20, 2022
@chipitsine
Copy link
Member Author

current detection log

1685int readcfgfile(const char *file)
1686{
1687        char *thisline = NULL;
1688        int linesize = LINESIZE;
1689        FILE *f = NULL;
1690        int linenum = 0;
1691        int err_code = 0;
1692        struct cfg_section *cs = NULL, *pcs = NULL;
1693        struct cfg_section *ics;
1694        int readbytes = 0;
1695        char *outline = NULL;
1696        size_t outlen = 0;
1697        size_t outlinesize = 0;
1698        int fatal = 0;
1699        int missing_lf = -1;
1700        int nested_cond_lvl = 0;
1701        enum nested_cond_state nested_conds[MAXNESTEDCONDS];
1702        int non_global_section_parsed = 0;
1703        char *errmsg = NULL;
1704
1705        global.cfg_curr_line = 0;
1706        global.cfg_curr_file = file;
1707
    	1. Condition (thisline = malloc(1UL /* sizeof (*thisline) */ * linesize)) == NULL, taking false branch.
1708        if ((thisline = malloc(sizeof(*thisline) * linesize)) == NULL) {
1709                ha_alert("Out of memory trying to allocate a buffer for a configuration line.\n");
1710                err_code = -1;
1711                goto err;
1712        }
1713
    	2. Condition (f = fopen(file, "r")) == NULL, taking false branch.
1714        if ((f = fopen(file,"r")) == NULL) {
1715                err_code = -1;
1716                goto err;
1717        }
1718
1719        /* change to the new dir if required */
    	3. Condition !cfg_apply_default_path(file, NULL, &errmsg), taking false branch.
1720        if (!cfg_apply_default_path(file, NULL, &errmsg)) {
1721                ha_alert("parsing [%s:%d]: failed to apply default-path: %s.\n", file, linenum, errmsg);
1722                free(errmsg);
1723                err_code = -1;
1724                goto err;
1725        }
1726
1727next_line:
    	4. Condition fgets(thisline + readbytes, linesize - readbytes, f) != NULL, taking true branch.
    	11. Condition fgets(thisline + readbytes, linesize - readbytes, f) != NULL, taking true branch.
    	18. Condition fgets(thisline + readbytes, linesize - readbytes, f) != NULL, taking true branch.
    	25. Condition fgets(thisline + readbytes, linesize - readbytes, f) != NULL, taking true branch.
    	32. Condition fgets(thisline + readbytes, linesize - readbytes, f) != NULL, taking true branch.
    	45. Condition fgets(thisline + readbytes, linesize - readbytes, f) != NULL, taking true branch.
    	52. Condition fgets(thisline + readbytes, linesize - readbytes, f) != NULL, taking true branch.
    	59. Condition fgets(thisline + readbytes, linesize - readbytes, f) != NULL, taking true branch.
    	72. Condition fgets(thisline + readbytes, linesize - readbytes, f) != NULL, taking true branch.
    	88. Condition fgets(thisline + readbytes, linesize - readbytes, f) != NULL, taking true branch.
    	101. Condition fgets(thisline + readbytes, linesize - readbytes, f) != NULL, taking true branch.
    	108. Condition fgets(thisline + readbytes, linesize - readbytes, f) != NULL, taking true branch.
    	135. Condition fgets(thisline + readbytes, linesize - readbytes, f) != NULL, taking true branch.
    	176. Condition fgets(thisline + readbytes, linesize - readbytes, f) != NULL, taking true branch.
    	208. Condition fgets(thisline + readbytes, linesize - readbytes, f) != NULL, taking true branch.
1728        while (fgets(thisline + readbytes, linesize - readbytes, f) != NULL) {
1729                int arg, kwm = KWM_STD;
1730                char *end;
1731                char *args[MAX_LINE_ARGS + 1];
1732                char *line = thisline;
1733
    	5. Condition missing_lf != -1, taking false branch.
    	12. Condition missing_lf != -1, taking false branch.
    	19. Condition missing_lf != -1, taking false branch.
    	26. Condition missing_lf != -1, taking false branch.
    	33. Condition missing_lf != -1, taking false branch.
    	46. Condition missing_lf != -1, taking false branch.
    	53. Condition missing_lf != -1, taking false branch.
    	60. Condition missing_lf != -1, taking false branch.
    	73. Condition missing_lf != -1, taking false branch.
    	89. Condition missing_lf != -1, taking false branch.
    	102. Condition missing_lf != -1, taking false branch.
    	109. Condition missing_lf != -1, taking false branch.
    	136. Condition missing_lf != -1, taking false branch.
    	177. Condition missing_lf != -1, taking false branch.
    	209. Condition missing_lf != -1, taking false branch.
1734                if (missing_lf != -1) {
1735                        ha_alert("parsing [%s:%d]: Stray NUL character at position %d.\n",
1736                                 file, linenum, (missing_lf + 1));
1737                        err_code |= ERR_ALERT | ERR_FATAL;
1738                        missing_lf = -1;
1739                        break;
1740                }
1741
1742                linenum++;
1743                global.cfg_curr_line = linenum;
1744
    	6. Condition fatal >= 50, taking false branch.
    	13. Condition fatal >= 50, taking false branch.
    	20. Condition fatal >= 50, taking false branch.
    	27. Condition fatal >= 50, taking false branch.
    	34. Condition fatal >= 50, taking false branch.
    	47. Condition fatal >= 50, taking false branch.
    	54. Condition fatal >= 50, taking false branch.
    	61. Condition fatal >= 50, taking false branch.
    	74. Condition fatal >= 50, taking false branch.
    	90. Condition fatal >= 50, taking false branch.
    	103. Condition fatal >= 50, taking false branch.
    	110. Condition fatal >= 50, taking false branch.
    	137. Condition fatal >= 50, taking false branch.
    	178. Condition fatal >= 50, taking false branch.
    	210. Condition fatal >= 50, taking false branch.
1745                if (fatal >= 50) {
1746                        ha_alert("parsing [%s:%d]: too many fatal errors (%d), stopping now.\n", file, linenum, fatal);
1747                        break;
1748                }
1749
1750                end = line + strlen(line);
1751
    	7. Condition end - line == linesize - 1, taking true branch.
    	8. Condition *(end - 1) != 10, taking true branch.
    	14. Condition end - line == linesize - 1, taking true branch.
    	15. Condition *(end - 1) != 10, taking true branch.
    	21. Condition end - line == linesize - 1, taking true branch.
    	22. Condition *(end - 1) != 10, taking true branch.
    	28. Condition end - line == linesize - 1, taking true branch.
    	29. Condition *(end - 1) != 10, taking true branch.
    	35. Condition end - line == linesize - 1, taking true branch.
    	36. Condition *(end - 1) != 10, taking false branch.
    	48. Condition end - line == linesize - 1, taking true branch.
    	49. Condition *(end - 1) != 10, taking true branch.
    	55. Condition end - line == linesize - 1, taking true branch.
    	56. Condition *(end - 1) != 10, taking true branch.
    	62. Condition end - line == linesize - 1, taking true branch.
    	63. Condition *(end - 1) != 10, taking false branch.
    	75. Condition end - line == linesize - 1, taking true branch.
    	76. Condition *(end - 1) != 10, taking false branch.
    	91. Condition end - line == linesize - 1, taking true branch.
    	92. Condition *(end - 1) != 10, taking false branch.
    	104. Condition end - line == linesize - 1, taking true branch.
    	105. Condition *(end - 1) != 10, taking true branch.
    	111. Condition end - line == linesize - 1, taking true branch.
    	112. Condition *(end - 1) != 10, taking false branch.
    	138. Condition end - line == linesize - 1, taking true branch.
    	139. Condition *(end - 1) != 10, taking false branch.
    	179. Condition end - line == linesize - 1, taking true branch.
    	180. Condition *(end - 1) != 10, taking false branch.
    	211. Condition end - line == linesize - 1, taking true branch.
    	212. Condition *(end - 1) != 10, taking false branch.
1752                if (end-line == linesize-1 && *(end-1) != '\n') {
1753                        /* Check if we reached the limit and the last char is not \n.
1754                         * Watch out for the last line without the terminating '\n'!
1755                         */
1756                        char *newline;
1757                        int newlinesize = linesize * 2;
1758
1759                        newline = realloc(thisline, sizeof(*thisline) * newlinesize);
    	9. Condition newline == NULL, taking false branch.
    	16. Condition newline == NULL, taking false branch.
    	23. Condition newline == NULL, taking true branch.
    	30. Condition newline == NULL, taking true branch.
    	50. Condition newline == NULL, taking false branch.
    	57. Condition newline == NULL, taking false branch.
    	106. Condition newline == NULL, taking false branch.
1760                        if (newline == NULL) {
1761                                ha_alert("parsing [%s:%d]: line too long, cannot allocate memory.\n",
1762                                         file, linenum);
1763                                err_code |= ERR_ALERT | ERR_FATAL;
1764                                fatal++;
1765                                linenum--;
    	24. Continuing loop.
    	31. Continuing loop.
1766                                continue;
1767                        }
1768
1769                        readbytes = linesize - 1;
1770                        linesize = newlinesize;
1771                        thisline = newline;
1772                        linenum--;
    	10. Continuing loop.
    	17. Continuing loop.
    	51. Continuing loop.
    	58. Continuing loop.
    	107. Continuing loop.
1773                        continue;
1774                }
1775
1776                readbytes = 0;
1777
    	37. Condition end > line, taking true branch.
    	38. Condition *(end - 1) == 10, taking true branch.
    	64. Condition end > line, taking true branch.
    	65. Condition *(end - 1) == 10, taking true branch.
    	77. Condition end > line, taking true branch.
    	78. Condition *(end - 1) == 10, taking true branch.
    	93. Condition end > line, taking true branch.
    	94. Condition *(end - 1) == 10, taking true branch.
    	113. Condition end > line, taking true branch.
    	114. Condition *(end - 1) == 10, taking true branch.
    	140. Condition end > line, taking true branch.
    	141. Condition *(end - 1) == 10, taking true branch.
    	181. Condition end > line, taking true branch.
    	182. Condition *(end - 1) == 10, taking true branch.
    	213. Condition end > line, taking true branch.
    	214. Condition *(end - 1) == 10, taking true branch.
1778                if (end > line && *(end-1) == '\n') {
1779                        /* kill trailing LF */
1780                        *(end - 1) = 0;
    	39. Falling through to end of if statement.
    	66. Falling through to end of if statement.
    	79. Falling through to end of if statement.
    	95. Falling through to end of if statement.
    	115. Falling through to end of if statement.
    	142. Falling through to end of if statement.
    	183. Falling through to end of if statement.
    	215. Falling through to end of if statement.
1781                }
1782                else {
1783                        /* mark this line as truncated */
1784                        missing_lf = end - line;
1785                }
1786
1787                /* skip leading spaces */
    	40. Condition *__ctype_b_loc()[(int)(unsigned char)*line] & 8192 /* (unsigned short)_ISspace */, taking true branch.
    	42. Condition *__ctype_b_loc()[(int)(unsigned char)*line] & 8192 /* (unsigned short)_ISspace */, taking false branch.
    	67. Condition *__ctype_b_loc()[(int)(unsigned char)*line] & 8192 /* (unsigned short)_ISspace */, taking true branch.
    	69. Condition *__ctype_b_loc()[(int)(unsigned char)*line] & 8192 /* (unsigned short)_ISspace */, taking false branch.
    	80. Condition *__ctype_b_loc()[(int)(unsigned char)*line] & 8192 /* (unsigned short)_ISspace */, taking false branch.
    	96. Condition *__ctype_b_loc()[(int)(unsigned char)*line] & 8192 /* (unsigned short)_ISspace */, taking true branch.
    	98. Condition *__ctype_b_loc()[(int)(unsigned char)*line] & 8192 /* (unsigned short)_ISspace */, taking false branch.
    	116. Condition *__ctype_b_loc()[(int)(unsigned char)*line] & 8192 /* (unsigned short)_ISspace */, taking true branch.
    	118. Condition *__ctype_b_loc()[(int)(unsigned char)*line] & 8192 /* (unsigned short)_ISspace */, taking false branch.
    	143. Condition *__ctype_b_loc()[(int)(unsigned char)*line] & 8192 /* (unsigned short)_ISspace */, taking true branch.
    	145. Condition *__ctype_b_loc()[(int)(unsigned char)*line] & 8192 /* (unsigned short)_ISspace */, taking false branch.
    	184. Condition *__ctype_b_loc()[(int)(unsigned char)*line] & 8192 /* (unsigned short)_ISspace */, taking true branch.
    	186. Condition *__ctype_b_loc()[(int)(unsigned char)*line] & 8192 /* (unsigned short)_ISspace */, taking false branch.
    	216. Condition *__ctype_b_loc()[(int)(unsigned char)*line] & 8192 /* (unsigned short)_ISspace */, taking true branch.
    	218. Condition *__ctype_b_loc()[(int)(unsigned char)*line] & 8192 /* (unsigned short)_ISspace */, taking false branch.
1788                while (isspace((unsigned char)*line))
    	41. Jumping back to the beginning of the loop.
    	68. Jumping back to the beginning of the loop.
    	97. Jumping back to the beginning of the loop.
    	117. Jumping back to the beginning of the loop.
    	144. Jumping back to the beginning of the loop.
    	185. Jumping back to the beginning of the loop.
    	217. Jumping back to the beginning of the loop.
1789                        line++;
1790
    	43. Condition *line == '[', taking true branch.
    	70. Condition *line == '[', taking true branch.
    	81. Condition *line == '[', taking false branch.
    	99. Condition *line == '[', taking true branch.
    	119. Condition *line == '[', taking false branch.
    	146. Condition *line == '[', taking false branch.
    	187. Condition *line == '[', taking false branch.
    	219. Condition *line == '[', taking false branch.
1791                if (*line == '[') {/* This is the beginning if a scope */
1792                        err_code |= cfg_parse_scope(file, linenum, line);
    	44. Jumping to label next_line.
    	71. Jumping to label next_line.
    	100. Jumping to label next_line.
1793                        goto next_line;
1794                }
1795
    	82. Condition 1, taking true branch.
    	120. Condition 1, taking true branch.
    	129. Condition 1, taking true branch.
    	147. Condition 1, taking true branch.
    	156. Condition 1, taking true branch.
    	188. Condition 1, taking true branch.
    	197. Condition 1, taking true branch.
    	220. Condition 1, taking true branch.
1796                while (1) {
1797                        uint32_t err;
1798                        const char *errptr;
1799
1800                        arg = sizeof(args) / sizeof(*args);
1801                        outlen = outlinesize;
      	CID 1429926: Explicit null dereferenced (FORWARD_NULL) [[select issue](https://scan6.scan.coverity.com/defectInstanceId=33271394&fileInstanceId=137473839&mergedDefectId=1429926)]
    	198. identity_transfer: Passing outline as argument 2 to function parse_line, which sets *args to that argument. [[show details](https://scan6.scan.coverity.com/eventId=33271395-319&modelId=33271395-0&fileInstanceId=137473652&filePath=%2Fsrc%2Ftools.c&fileStart=5342&fileEnd=5635)]
1802                        err = parse_line(line, outline, &outlen, args, &arg,
1803                                         PARSE_OPT_ENV | PARSE_OPT_DQUOTE | PARSE_OPT_SQUOTE |
1804                                         PARSE_OPT_BKSLASH | PARSE_OPT_SHARP | PARSE_OPT_WORD_EXPAND,
1805                                         &errptr);
1806
    	83. Condition err & 4, taking false branch.
    	121. Condition err & 4, taking false branch.
    	130. Condition err & 4, taking false branch.
    	148. Condition err & 4, taking false branch.
    	157. Condition err & 4, taking false branch.
    	189. Condition err & 4, taking false branch.
    	199. Condition err & 4, taking false branch.
    	221. Condition err & 4, taking false branch.
1807                        if (err & PARSE_ERR_QUOTE) {
1808                                size_t newpos = sanitize_for_printing(line, errptr - line, 80);
1809
1810                                ha_alert("parsing [%s:%d]: unmatched quote at position %d:\n"
1811                                         "  %s\n  %*s\n", file, linenum, (int)(errptr-thisline+1), line, (int)(newpos+1), "^");
1812                                err_code |= ERR_ALERT | ERR_FATAL;
1813                                fatal++;
1814                                goto next_line;
1815                        }
1816
    	84. Condition err & 8, taking false branch.
    	122. Condition err & 8, taking false branch.
    	131. Condition err & 8, taking false branch.
    	149. Condition err & 8, taking false branch.
    	158. Condition err & 8, taking false branch.
    	190. Condition err & 8, taking false branch.
    	200. Condition err & 8, taking false branch.
    	222. Condition err & 8, taking false branch.
1817                        if (err & PARSE_ERR_BRACE) {
1818                                size_t newpos = sanitize_for_printing(line, errptr - line, 80);
1819
1820                                ha_alert("parsing [%s:%d]: unmatched brace in environment variable name at position %d:\n"
1821                                         "  %s\n  %*s\n", file, linenum, (int)(errptr-thisline+1), line, (int)(newpos+1), "^");
1822                                err_code |= ERR_ALERT | ERR_FATAL;
1823                                fatal++;
1824                                goto next_line;
1825                        }
1826
    	85. Condition err & 32, taking false branch.
    	123. Condition err & 32, taking false branch.
    	132. Condition err & 32, taking false branch.
    	150. Condition err & 32, taking false branch.
    	159. Condition err & 32, taking false branch.
    	191. Condition err & 32, taking false branch.
    	201. Condition err & 32, taking false branch.
    	223. Condition err & 32, taking false branch.
1827                        if (err & PARSE_ERR_VARNAME) {
1828                                size_t newpos = sanitize_for_printing(line, errptr - line, 80);
1829
1830                                ha_alert("parsing [%s:%d]: forbidden first char in environment variable name at position %d:\n"
1831                                         "  %s\n  %*s\n", file, linenum, (int)(errptr-thisline+1), line, (int)(newpos+1), "^");
1832                                err_code |= ERR_ALERT | ERR_FATAL;
1833                                fatal++;
1834                                goto next_line;
1835                        }
1836
    	86. Condition err & 16, taking true branch.
    	124. Condition err & 16, taking false branch.
    	133. Condition err & 16, taking true branch.
    	151. Condition err & 16, taking false branch.
    	160. Condition err & 16, taking false branch.
    	192. Condition err & 16, taking false branch.
    	202. Condition err & 16, taking false branch.
    	224. Condition err & 16, taking false branch.
1837                        if (err & PARSE_ERR_HEX) {
1838                                size_t newpos = sanitize_for_printing(line, errptr - line, 80);
1839
1840                                ha_alert("parsing [%s:%d]: truncated or invalid hexadecimal sequence at position %d:\n"
1841                                         "  %s\n  %*s\n", file, linenum, (int)(errptr-thisline+1), line, (int)(newpos+1), "^");
1842                                err_code |= ERR_ALERT | ERR_FATAL;
1843                                fatal++;
    	87. Jumping to label next_line.
    	134. Jumping to label next_line.
1844                                goto next_line;
1845                        }
1846
    	125. Condition err & 128, taking false branch.
    	152. Condition err & 128, taking false branch.
    	161. Condition err & 128, taking false branch.
    	193. Condition err & 128, taking false branch.
    	203. Condition err & 128, taking false branch.
    	225. Condition err & 128, taking false branch.
1847                        if (err & PARSE_ERR_WRONG_EXPAND) {
1848                                size_t newpos = sanitize_for_printing(line, errptr - line, 80);
1849
1850                                ha_alert("parsing [%s:%d]: truncated or invalid word expansion sequence at position %d:\n"
1851                                         "  %s\n  %*s\n", file, linenum, (int)(errptr-thisline+1), line, (int)(newpos+1), "^");
1852                                err_code |= ERR_ALERT | ERR_FATAL;
1853                                fatal++;
1854                                goto next_line;
1855                        }
1856
    	126. Condition err & (65U /* 1 | 0x40 */), taking true branch.
    	153. Condition err & (65U /* 1 | 0x40 */), taking true branch.
    	162. Condition err & (65U /* 1 | 0x40 */), taking false branch.
    	194. Condition err & (65U /* 1 | 0x40 */), taking true branch.
    	204. Condition err & (65U /* 1 | 0x40 */), taking true branch.
    	226. Condition err & (65U /* 1 | 0x40 */), taking false branch.
1857                        if (err & (PARSE_ERR_TOOLARGE|PARSE_ERR_OVERLAP)) {
1858                                outlinesize = (outlen + 1023) & -1024;
    	205. freed_arg: my_realloc2 frees outline. [[show details](https://scan6.scan.coverity.com/eventId=33271395-560&modelId=33271395-1&fileInstanceId=137473239&filePath=%2Finclude%2Fhaproxy%2Ftools.h&fileStart=1017&fileEnd=1025)]
1859                                outline = my_realloc2(outline, outlinesize);
    	127. Condition outline == NULL, taking false branch.
    	154. Condition outline == NULL, taking false branch.
    	195. Condition outline == NULL, taking false branch.
    	206. Condition outline == NULL, taking true branch.
1860                                if (outline == NULL) {
1861                                        ha_alert("parsing [%s:%d]: line too long, cannot allocate memory.\n",
1862                                                 file, linenum);
1863                                        err_code |= ERR_ALERT | ERR_FATAL;
1864                                        fatal++;
1865                                        outlinesize = 0;
    	207. Jumping to label next_line.
1866                                        goto next_line;
1867                                }
1868                                /* try again */
    	128. Continuing loop.
    	155. Continuing loop.
    	196. Continuing loop.
1869                                continue;
1870                        }
1871
    	163. Condition err & 2, taking false branch.
    	227. Condition err & 2, taking false branch.
1872                        if (err & PARSE_ERR_TOOMANY) {
1873                                /* only check this *after* being sure the output is allocated */
1874                                ha_alert("parsing [%s:%d]: too many words, truncating after word %d, position %ld: <%s>.\n",
1875                                         file, linenum, MAX_LINE_ARGS, (long)(args[MAX_LINE_ARGS-1] - outline + 1), args[MAX_LINE_ARGS-1]);
1876                                err_code |= ERR_ALERT | ERR_FATAL;
1877                                fatal++;
1878                                goto next_line;
1879                        }
1880
1881                        /* everything's OK */
    	164. Breaking from loop.
    	228. Breaking from loop.
1882                        break;
1883                }
1884
1885                /* empty line */
    	165. Condition !**args, taking false branch.
    	
CID 1469673 (#1 of 1): Read from pointer after free (USE_AFTER_FREE)
229. deref_after_free: Dereferencing freed pointer *args.
1886                if (!**args)
1887                        continue;
1888
1889                /* check for config macros */
    	166. Condition *args[0] == '.', taking true branch.
1890                if (*args[0] == '.') {
    	167. Condition strcmp(args[0], ".if") == 0, taking true branch.
1891                        if (strcmp(args[0], ".if") == 0) {
1892                                const char *errptr = NULL;
1893                                char *errmsg = NULL;
1894                                int cond;
1895                                char *w;
1896
1897                                /* remerge all words into a single expression */
    	168. Condition (w += strlen(w)) < outline + outlen - 1, taking true branch.
    	170. Condition (w += strlen(w)) < outline + outlen - 1, taking false branch.
1898                                for (w = *args; (w += strlen(w)) < outline + outlen - 1; *w = ' ')
    	169. Jumping back to the beginning of the loop.
1899                                        ;
1900
1901                                nested_cond_lvl++;
    	171. Condition nested_cond_lvl >= 100, taking false branch.
1902                                if (nested_cond_lvl >= MAXNESTEDCONDS) {
1903                                        ha_alert("parsing [%s:%d]: too many nested '.if', max is %d.\n", file, linenum, MAXNESTEDCONDS);
1904                                        err_code |= ERR_ALERT | ERR_FATAL | ERR_ABORT;
1905                                        goto err;
1906                                }
1907
    	172. Condition nested_cond_lvl > 1, taking false branch.
1908                                if (nested_cond_lvl > 1 &&
1909                                    (nested_conds[nested_cond_lvl - 1] == NESTED_COND_IF_DROP ||
1910                                     nested_conds[nested_cond_lvl - 1] == NESTED_COND_IF_SKIP ||
1911                                     nested_conds[nested_cond_lvl - 1] == NESTED_COND_ELIF_DROP ||
1912                                     nested_conds[nested_cond_lvl - 1] == NESTED_COND_ELIF_SKIP ||
1913                                     nested_conds[nested_cond_lvl - 1] == NESTED_COND_ELSE_DROP)) {
1914                                        nested_conds[nested_cond_lvl] = NESTED_COND_IF_SKIP;
1915                                        goto next_line;
1916                                }
1917
1918                                cond = cfg_eval_condition(args + 1, &errmsg, &errptr);
    	173. Condition cond < 0, taking false branch.
1919                                if (cond < 0) {
1920                                        size_t newpos = sanitize_for_printing(args[1], errptr - args[1], 76);
1921
1922                                        ha_alert("parsing [%s:%d]: %s in '.if' at position %d:\n  .if %s\n  %*s\n",
1923                                                 file, linenum, errmsg,
1924                                                 (int)(errptr-args[1]+1), args[1], (int)(newpos+5), "^");
1925
1926                                        free(errmsg);
1927                                        err_code |= ERR_ALERT | ERR_FATAL | ERR_ABORT;
1928                                        goto err;
1929                                }
1930
    	174. Condition cond, taking false branch.
1931                                if (cond)
1932                                        nested_conds[nested_cond_lvl] = NESTED_COND_IF_TAKE;
1933                                else
1934                                        nested_conds[nested_cond_lvl] = NESTED_COND_IF_DROP;
1935
    	175. Jumping to label next_line.
1936                                goto next_line;
1937                        }
1938                        else if (strcmp(args[0], ".elif") == 0) {
1939                                const char *errptr = NULL;
1940                                char *errmsg = NULL;
1941                                int cond;
1942                                char *w;
1943
1944                                /* remerge all words into a single expression */
1945                                for (w = *args; (w += strlen(w)) < outline + outlen - 1; *w = ' ')
1946                                        ;
1947
1948                                if (!nested_cond_lvl) {
1949                                        ha_alert("parsing [%s:%d]: lone '.elif' with no matching '.if'.\n", file, linenum);
1950                                        err_code |= ERR_ALERT | ERR_FATAL | ERR_ABORT;
1951                                        goto err;
1952                                }
1953
1954                                if (nested_conds[nested_cond_lvl] == NESTED_COND_ELSE_TAKE ||
1955                                    nested_conds[nested_cond_lvl] == NESTED_COND_ELSE_DROP) {
1956                                        ha_alert("parsing [%s:%d]: '.elif' after '.else' is not permitted.\n", file, linenum);
1957                                        err_code |= ERR_ALERT | ERR_FATAL | ERR_ABORT;
1958                                        goto err;
1959                                }
1960
1961                                if (nested_conds[nested_cond_lvl] == NESTED_COND_IF_TAKE ||
1962                                    nested_conds[nested_cond_lvl] == NESTED_COND_IF_SKIP ||
1963                                    nested_conds[nested_cond_lvl] == NESTED_COND_ELIF_TAKE ||
1964                                    nested_conds[nested_cond_lvl] == NESTED_COND_ELIF_SKIP) {
1965                                        nested_conds[nested_cond_lvl] = NESTED_COND_ELIF_SKIP;
1966                                        goto next_line;
1967                                }
1968
1969                                cond = cfg_eval_condition(args + 1, &errmsg, &errptr);
1970                                if (cond < 0) {
1971                                        size_t newpos = sanitize_for_printing(args[1], errptr - args[1], 74);
1972
1973                                        ha_alert("parsing [%s:%d]: %s in '.elif' at position %d:\n  .elif %s\n  %*s\n",
1974                                                 file, linenum, errmsg,
1975                                                 (int)(errptr-args[1]+1), args[1], (int)(newpos+7), "^");
1976
1977                                        free(errmsg);
1978                                        err_code |= ERR_ALERT | ERR_FATAL | ERR_ABORT;
1979                                        goto err;
1980                                }
1981
1982                                if (cond)
1983                                        nested_conds[nested_cond_lvl] = NESTED_COND_ELIF_TAKE;
1984                                else
1985                                        nested_conds[nested_cond_lvl] = NESTED_COND_ELIF_DROP;
1986
1987                                goto next_line;
1988                        }
1989                        else if (strcmp(args[0], ".else") == 0) {
1990                                if (*args[1]) {
1991                                        ha_alert("parsing [%s:%d]: Unexpected argument '%s' for '%s'.\n",
1992                                                 file, linenum, args[1], args[0]);
1993                                        err_code |= ERR_ALERT | ERR_FATAL | ERR_ABORT;
1994                                        break;
1995                                }
1996

haproxy-mirror pushed a commit that referenced this issue May 20, 2022
In issue #1563, Coverity reported a very interesting issue about a
possible UAF in the config parser if the config file ends in with a
very large line followed by an empty one and the large one causes an
allocation failure.

The issue essentially is that we try to go on with the next line in case
of allocation error, while there's no point doing so. If we failed to
allocate memory to read one config line, the same may happen on the next
one, and blatantly dropping it while trying to parse what follows it. In
the best case, subsequent errors will be incorrect due to this prior error
(e.g. a large ACL definition with many patterns, followed by a reference of
this ACL).

Let's just immediately abort in such a condition where there's no recovery
possible.

This may be backported to all versions once the issue is confirmed to be
addressed.

Thanks to Ilya for the report.
@wtarreau
Copy link
Member

Thank you Ilya, this one is valid and interesting in addition :-)
I think 8ec9c81 should address it now.

@chipitsine
Copy link
Member Author

chipitsine commented May 20, 2022 via email

@chipitsine
Copy link
Member Author

coverity confirms it is fixed

@wtarreau
Copy link
Member

Cool, thanks Ilya!

@capflam capflam added status: fixed This issue is a now-fixed bug. and removed dev This issue affects the HAProxy development branch. status: reviewed This issue was reviewed. A fix is required. labels May 23, 2022
FireBurn pushed a commit to FireBurn/haproxy that referenced this issue Jun 8, 2022
…dcfgfile()

When the line parsing failed because outline buffer must be reallocated, if
my_realloc2() call fails, the buffer size must be reset. Indeed, in this case
the current line is skipped, a fatal error is reported and we jump to the next
line. At this stage the outline buffer is NULL. If the buffer size is not reset,
the next call to parse_line() crashes because we try to write in the buffer. We
fail to detect the outline buffer is too small to copy any character.

To fix the issue, outlinesize variable must be set to 0 when outline allocation
failed.

This patch should fix the issue haproxy#1563. It must be backported as far as 2.2.

(cherry picked from commit dfe32c7)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
(cherry picked from commit 693e644)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
(cherry picked from commit 6ae99cb)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
FireBurn pushed a commit to FireBurn/haproxy that referenced this issue Jun 8, 2022
In issue haproxy#1563, Coverity reported a very interesting issue about a
possible UAF in the config parser if the config file ends in with a
very large line followed by an empty one and the large one causes an
allocation failure.

The issue essentially is that we try to go on with the next line in case
of allocation error, while there's no point doing so. If we failed to
allocate memory to read one config line, the same may happen on the next
one, and blatantly dropping it while trying to parse what follows it. In
the best case, subsequent errors will be incorrect due to this prior error
(e.g. a large ACL definition with many patterns, followed by a reference of
this ACL).

Let's just immediately abort in such a condition where there's no recovery
possible.

This may be backported to all versions once the issue is confirmed to be
addressed.

Thanks to Ilya for the report.

(cherry picked from commit 8ec9c81)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
(cherry picked from commit dbcd9db)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
(cherry picked from commit 0c68be7)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
FireBurn pushed a commit to FireBurn/haproxy that referenced this issue Jun 8, 2022
…dcfgfile()

When the line parsing failed because outline buffer must be reallocated, if
my_realloc2() call fails, the buffer size must be reset. Indeed, in this case
the current line is skipped, a fatal error is reported and we jump to the next
line. At this stage the outline buffer is NULL. If the buffer size is not reset,
the next call to parse_line() crashes because we try to write in the buffer. We
fail to detect the outline buffer is too small to copy any character.

To fix the issue, outlinesize variable must be set to 0 when outline allocation
failed.

This patch should fix the issue haproxy#1563. It must be backported as far as 2.2.

(cherry picked from commit dfe32c7)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
(cherry picked from commit 693e644)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
FireBurn pushed a commit to FireBurn/haproxy that referenced this issue Jun 8, 2022
In issue haproxy#1563, Coverity reported a very interesting issue about a
possible UAF in the config parser if the config file ends in with a
very large line followed by an empty one and the large one causes an
allocation failure.

The issue essentially is that we try to go on with the next line in case
of allocation error, while there's no point doing so. If we failed to
allocate memory to read one config line, the same may happen on the next
one, and blatantly dropping it while trying to parse what follows it. In
the best case, subsequent errors will be incorrect due to this prior error
(e.g. a large ACL definition with many patterns, followed by a reference of
this ACL).

Let's just immediately abort in such a condition where there's no recovery
possible.

This may be backported to all versions once the issue is confirmed to be
addressed.

Thanks to Ilya for the report.

(cherry picked from commit 8ec9c81)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
(cherry picked from commit dbcd9db)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
FireBurn pushed a commit to FireBurn/haproxy that referenced this issue Jun 8, 2022
…dcfgfile()

When the line parsing failed because outline buffer must be reallocated, if
my_realloc2() call fails, the buffer size must be reset. Indeed, in this case
the current line is skipped, a fatal error is reported and we jump to the next
line. At this stage the outline buffer is NULL. If the buffer size is not reset,
the next call to parse_line() crashes because we try to write in the buffer. We
fail to detect the outline buffer is too small to copy any character.

To fix the issue, outlinesize variable must be set to 0 when outline allocation
failed.

This patch should fix the issue haproxy#1563. It must be backported as far as 2.2.

(cherry picked from commit dfe32c7)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
FireBurn pushed a commit to FireBurn/haproxy that referenced this issue Jun 8, 2022
In issue haproxy#1563, Coverity reported a very interesting issue about a
possible UAF in the config parser if the config file ends in with a
very large line followed by an empty one and the large one causes an
allocation failure.

The issue essentially is that we try to go on with the next line in case
of allocation error, while there's no point doing so. If we failed to
allocate memory to read one config line, the same may happen on the next
one, and blatantly dropping it while trying to parse what follows it. In
the best case, subsequent errors will be incorrect due to this prior error
(e.g. a large ACL definition with many patterns, followed by a reference of
this ACL).

Let's just immediately abort in such a condition where there's no recovery
possible.

This may be backported to all versions once the issue is confirmed to be
addressed.

Thanks to Ilya for the report.

(cherry picked from commit 8ec9c81)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
@capflam capflam closed this as completed Jun 13, 2022
@capflam capflam removed 2.2 This issue affects the HAProxy 2.2 stable branch. 2.4 This issue affects the HAProxy 2.4 stable branch. 2.5 This issue affects the HAProxy 2.5 stable branch. labels Jun 13, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: fixed This issue is a now-fixed bug. type: code-report This issue describes a code report (like valgrind or coverity)
Projects
None yet
Development

No branches or pull requests

3 participants