File cfgparse.c has the following code:

cond = cfg_eval_condition(args + 1, &errmsg, &errptr);
if (cond < 0) {
        size_t newpos = sanitize_for_printing(args[1], errptr - args[1], 76);

It assumes that errptr is always written, but cfg_eval_condition
doesn't always write to the errptr if cfg_eval_cond_expr fails.
And cfg_eval_cond_expr could fail because cfg_eval_cond_term could fail.

HAProxy segfaults currently if cfg has the following:

.if openssl_version_atleast(1.1)
.endif

.if awslc_api_atleast(whatever)
.endif

This patch fixes that problem.

Ideally cfgparse.c would be rewritten, so such an UB wouldn't be
possible at all, but here I just changed the cfgcond.c code so it
actually writes the errptr and err.
---
 src/cfgcond.c | 33 ++++++++++++++++++++++++---------
 1 file changed, 24 insertions(+), 9 deletions(-)

diff --git a/src/cfgcond.c b/src/cfgcond.c
index 7be2e7a47..2dc28846e 100644
--- a/src/cfgcond.c
+++ b/src/cfgcond.c
@@ -272,37 +272,45 @@ int cfg_eval_cond_term(const struct cfg_cond_term *term, 
char **err)
                case CFG_PRED_OSSL_VERSION_ATLEAST: { // checks if the current 
openssl version is at least this one
                        int opensslret = 
openssl_compare_current_version(term->args[0].data.str.area);
 
-                       if (opensslret < -1) /* can't parse the string or no 
openssl available */
+                       if (opensslret < -1) {
+                               memprintf(err, "can't parse the version string 
or no OpenSSL available");
                                ret = -1;
-                       else
+                       } else {
                                ret = opensslret <= 0;
+                       }
                        break;
                }
                case CFG_PRED_OSSL_VERSION_BEFORE: { // checks if the current 
openssl version is older than this one
                        int opensslret = 
openssl_compare_current_version(term->args[0].data.str.area);
 
-                       if (opensslret < -1) /* can't parse the string or no 
openssl available */
+                       if (opensslret < -1) {
+                               memprintf(err, "can't parse the version string 
or no OpenSSL available");
                                ret = -1;
-                       else
+                       } else {
                                ret = opensslret > 0;
+                       }
                        break;
                }
                case CFG_PRED_AWSLC_API_ATLEAST: { // checks if the current 
AWSLC API is at least this one
                        int awslcret = 
awslc_compare_current_api(term->args[0].data.str.area);
 
-                       if (awslcret < -1) /* can't parse the string or no 
AWS-LC available */
+                       if (awslcret < -1) {
+                               memprintf(err, "can't parse the API version 
string or no AWS-LC available");
                                ret = -1;
-                       else
+                       } else {
                                ret = awslcret <= 0;
+                       }
                        break;
                }
                case CFG_PRED_AWSLC_API_BEFORE: { // checks if the current 
AWSLC API is older than this one
                        int awslcret = 
awslc_compare_current_api(term->args[0].data.str.area);
 
-                       if (awslcret < -1) /* can't parse the string or no 
AWS-LC available */
+                       if (awslcret < -1) {
+                               memprintf(err, "can't parse the API version 
string or no AWS-LC available");
                                ret = -1;
-                       else
+                       } else {
                                ret = awslcret > 0;
+                       }
                        break;
                }
                case CFG_PRED_SSLLIB_NAME_STARTSWITH: { // checks if the 
current SSL library's name starts with a specified string (can be used to 
distinguish OpenSSL from LibreSSL or BoringSSL)
@@ -327,6 +335,10 @@ int cfg_eval_cond_term(const struct cfg_cond_term *term, 
char **err)
 
        if (ret >= 0 && term->neg)
                ret = !ret;
+
+       if (ret < 0 && err != NULL && *err == NULL)
+               memprintf(err, "unknown error");
+
        return ret;
 }
 
@@ -564,7 +576,10 @@ int cfg_eval_condition(char **args, char **err, const char 
**errptr)
                }
 
                ret = cfg_eval_cond_expr(expr, err);
-               goto done;
+               if (ret > 0)
+                       goto done;
+               else
+                       goto fail;
        }
 
        /* ret == 0, no other way to parse this */
-- 
2.52.0


Reply via email to