mirror of
https://github.com/apache/httpd.git
synced 2025-08-15 23:27:39 +00:00
Limit recursion in ap_expr evaluation to avoid unbounded stack usage
* evaluate chains of ||, &&, and string concatenation non-recursively * limit other types of recursion to 20 levels * avoid some string copies if concatenating more than 2 strings git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1204087 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
@ -130,6 +130,8 @@ typedef struct {
|
||||
const char **result_string;
|
||||
/** Arbitrary context data provided by the caller for custom functions */
|
||||
void *data;
|
||||
/** The current recursion level */
|
||||
int reclvl;
|
||||
} ap_expr_eval_ctx_t;
|
||||
|
||||
/**
|
||||
|
@ -366,6 +366,7 @@
|
||||
* and ap_unescape_urlencoded().
|
||||
* 20111025.2 (2.3.15-dev) Add ap_lua_ssl_val to mod_lua
|
||||
* 20111118.0 (2.5.0-dev) Add conn_rec to error_log hook
|
||||
* 20111118.1 (2.5.0-dev) Add reclvl to ap_expr_eval_ctx_t
|
||||
*/
|
||||
|
||||
#define MODULE_MAGIC_COOKIE 0x41503234UL /* "AP24" */
|
||||
@ -373,7 +374,7 @@
|
||||
#ifndef MODULE_MAGIC_NUMBER_MAJOR
|
||||
#define MODULE_MAGIC_NUMBER_MAJOR 20111118
|
||||
#endif
|
||||
#define MODULE_MAGIC_NUMBER_MINOR 0 /* 0...n */
|
||||
#define MODULE_MAGIC_NUMBER_MINOR 1 /* 0...n */
|
||||
|
||||
/**
|
||||
* Determine if the server's current MODULE_MAGIC_NUMBER is at least a
|
||||
|
@ -56,10 +56,30 @@ static void expr_dump_tree(const ap_expr_t *e, const server_rec *s,
|
||||
int loglevel, int indent);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* To reduce counting overhead, we only count calls to
|
||||
* ap_expr_eval_word() and ap_expr_eval(). This means the actual
|
||||
* recursion may be twice as deep.
|
||||
*/
|
||||
#define AP_EXPR_MAX_RECURSION 20
|
||||
static int inc_rec(ap_expr_eval_ctx_t *ctx)
|
||||
{
|
||||
if (ctx->reclvl < AP_EXPR_MAX_RECURSION) {
|
||||
ctx->reclvl++;
|
||||
return 0;
|
||||
}
|
||||
*ctx->err = "Recursion limit reached";
|
||||
/* short circuit further evaluation */
|
||||
ctx->reclvl = INT_MAX;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const char *ap_expr_eval_word(ap_expr_eval_ctx_t *ctx,
|
||||
const ap_expr_t *node)
|
||||
{
|
||||
const char *result = "";
|
||||
if (inc_rec(ctx))
|
||||
return result;
|
||||
switch (node->node_op) {
|
||||
case op_Digit:
|
||||
case op_String:
|
||||
@ -68,17 +88,41 @@ static const char *ap_expr_eval_word(ap_expr_eval_ctx_t *ctx,
|
||||
case op_Var:
|
||||
result = ap_expr_eval_var(ctx, node->node_arg1, node->node_arg2);
|
||||
break;
|
||||
case op_Concat: {
|
||||
const char *s1 = ap_expr_eval_word(ctx, node->node_arg1);
|
||||
const char *s2 = ap_expr_eval_word(ctx, node->node_arg2);
|
||||
if (!*s1)
|
||||
result = s2;
|
||||
else if (!*s2)
|
||||
result = s1;
|
||||
else
|
||||
result = apr_pstrcat(ctx->p, s1, s2, NULL);
|
||||
case op_Concat:
|
||||
if (((ap_expr_t *)node->node_arg2)->node_op != op_Concat) {
|
||||
const char *s1 = ap_expr_eval_word(ctx, node->node_arg1);
|
||||
const char *s2 = ap_expr_eval_word(ctx, node->node_arg2);
|
||||
if (!*s1)
|
||||
result = s2;
|
||||
else if (!*s2)
|
||||
result = s1;
|
||||
else
|
||||
result = apr_pstrcat(ctx->p, s1, s2, NULL);
|
||||
}
|
||||
else {
|
||||
const ap_expr_t *nodep = node;
|
||||
int i = 1;
|
||||
struct iovec *vec;
|
||||
do {
|
||||
nodep = nodep->node_arg2;
|
||||
i++;
|
||||
} while (nodep->node_op == op_Concat);
|
||||
vec = apr_palloc(ctx->p, i * sizeof(struct iovec));
|
||||
nodep = node;
|
||||
i = 0;
|
||||
do {
|
||||
vec[i].iov_base = (void *)ap_expr_eval_word(ctx,
|
||||
nodep->node_arg1);
|
||||
vec[i].iov_len = strlen(vec[i].iov_base);
|
||||
i++;
|
||||
nodep = nodep->node_arg2;
|
||||
} while (nodep->node_op == op_Concat);
|
||||
vec[i].iov_base = (void *)ap_expr_eval_word(ctx, nodep);
|
||||
vec[i].iov_len = strlen(vec[i].iov_base);
|
||||
i++;
|
||||
result = apr_pstrcatv(ctx->p, vec, i, NULL);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case op_StringFuncCall: {
|
||||
const ap_expr_t *info = node->node_arg1;
|
||||
const ap_expr_t *args = node->node_arg2;
|
||||
@ -96,6 +140,7 @@ static const char *ap_expr_eval_word(ap_expr_eval_ctx_t *ctx,
|
||||
}
|
||||
if (!result)
|
||||
result = "";
|
||||
ctx->reclvl--;
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -657,30 +702,81 @@ static int ap_expr_eval(ap_expr_eval_ctx_t *ctx, const ap_expr_t *node)
|
||||
{
|
||||
const ap_expr_t *e1 = node->node_arg1;
|
||||
const ap_expr_t *e2 = node->node_arg2;
|
||||
switch (node->node_op) {
|
||||
case op_True:
|
||||
return 1;
|
||||
case op_False:
|
||||
return 0;
|
||||
case op_Not:
|
||||
return (!ap_expr_eval(ctx, e1));
|
||||
case op_Or:
|
||||
return (ap_expr_eval(ctx, e1) || ap_expr_eval(ctx, e2));
|
||||
case op_And:
|
||||
return (ap_expr_eval(ctx, e1) && ap_expr_eval(ctx, e2));
|
||||
case op_UnaryOpCall:
|
||||
return ap_expr_eval_unary_op(ctx, e1, e2);
|
||||
case op_BinaryOpCall:
|
||||
return ap_expr_eval_binary_op(ctx, e1, e2);
|
||||
case op_Comp:
|
||||
if (ctx->info->flags & AP_EXPR_FLAG_SSL_EXPR_COMPAT)
|
||||
return ssl_expr_eval_comp(ctx, e1);
|
||||
else
|
||||
return ap_expr_eval_comp(ctx, e1);
|
||||
default:
|
||||
*ctx->err = "Internal evaluation error: Unknown expression node";
|
||||
return FALSE;
|
||||
int result = FALSE;
|
||||
if (inc_rec(ctx))
|
||||
return result;
|
||||
while (1) {
|
||||
switch (node->node_op) {
|
||||
case op_True:
|
||||
result ^= TRUE;
|
||||
goto out;
|
||||
case op_False:
|
||||
ctx->reclvl--;
|
||||
result ^= FALSE;
|
||||
goto out;
|
||||
case op_Not:
|
||||
result = !result;
|
||||
node = e1;
|
||||
break;
|
||||
case op_Or:
|
||||
do {
|
||||
if (e1->node_op == op_Not) {
|
||||
if (!ap_expr_eval(ctx, e1->node_arg1)) {
|
||||
result ^= TRUE;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (ap_expr_eval(ctx, e1)) {
|
||||
ctx->reclvl--;
|
||||
result ^= TRUE;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
node = node->node_arg2;
|
||||
e1 = node->node_arg1;
|
||||
} while (node->node_op == op_Or);
|
||||
break;
|
||||
case op_And:
|
||||
do {
|
||||
if (e1->node_op == op_Not) {
|
||||
if (ap_expr_eval(ctx, e1->node_arg1)) {
|
||||
result ^= FALSE;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!ap_expr_eval(ctx, e1)) {
|
||||
result ^= FALSE;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
node = node->node_arg2;
|
||||
e1 = node->node_arg1;
|
||||
} while (node->node_op == op_And);
|
||||
break;
|
||||
case op_UnaryOpCall:
|
||||
result ^= ap_expr_eval_unary_op(ctx, e1, e2);
|
||||
goto out;
|
||||
case op_BinaryOpCall:
|
||||
result ^= ap_expr_eval_binary_op(ctx, e1, e2);
|
||||
goto out;
|
||||
case op_Comp:
|
||||
if (ctx->info->flags & AP_EXPR_FLAG_SSL_EXPR_COMPAT)
|
||||
result ^= ssl_expr_eval_comp(ctx, e1);
|
||||
else
|
||||
result ^= ap_expr_eval_comp(ctx, e1);
|
||||
goto out;
|
||||
default:
|
||||
*ctx->err = "Internal evaluation error: Unknown expression node";
|
||||
goto out;
|
||||
}
|
||||
e1 = node->node_arg1;
|
||||
e2 = node->node_arg2;
|
||||
}
|
||||
out:
|
||||
ctx->reclvl--;
|
||||
return result;
|
||||
}
|
||||
|
||||
AP_DECLARE(int) ap_expr_exec(request_rec *r, const ap_expr_info_t *info,
|
||||
@ -704,6 +800,7 @@ AP_DECLARE(int) ap_expr_exec_ctx(ap_expr_eval_ctx_t *ctx)
|
||||
AP_DEBUG_ASSERT(ctx->re_source != NULL);
|
||||
AP_DEBUG_ASSERT(ctx->re_nmatch > 0);
|
||||
}
|
||||
ctx->reclvl = 0;
|
||||
|
||||
*ctx->err = NULL;
|
||||
if (ctx->info->flags & AP_EXPR_FLAG_STRING_RESULT) {
|
||||
|
@ -591,9 +591,9 @@ static const yytype_int8 yypact[] =
|
||||
25, -35, 79, -17, -35, -8, 60, 60, 43, 43,
|
||||
43, 43, 43, 43, 43, 5, 5, 0, 43, 43,
|
||||
43, 43, 43, 43, 43, -35, -27, -35, -35, 73,
|
||||
-35, 3, -35, 25, 25, 25, 25, 25, 25, 25,
|
||||
-35, 86, 3, 25, 25, 25, 25, 25, 25, 25,
|
||||
-35, -35, -35, -35, 23, 43, -35, -35, 25, 25,
|
||||
25, 25, 25, 25, -35, -35, 106, 43, 85, 25,
|
||||
25, 25, 25, 25, 25, -35, 106, 43, 85, 25,
|
||||
-35, -21, -35, 43, -35, 25
|
||||
};
|
||||
|
||||
|
@ -81,10 +81,10 @@
|
||||
%token T_OP_AND
|
||||
%token T_OP_NOT
|
||||
|
||||
%left T_OP_OR
|
||||
%left T_OP_AND
|
||||
%left T_OP_NOT
|
||||
%left T_OP_CONCAT
|
||||
%right T_OP_OR
|
||||
%right T_OP_AND
|
||||
%right T_OP_NOT
|
||||
%right T_OP_CONCAT
|
||||
|
||||
%type <exVal> expr
|
||||
%type <exVal> comparison
|
||||
|
Reference in New Issue
Block a user