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