1 commit 47133bd99225554519c1d32293e0e5c3db83db30
2 Author: Willy Tarreau <w@1wt.eu>
3 Date: Tue Jun 4 16:27:36 2019 +0200
5 BUG/MEDIUM: vars: make sure the scope is always valid when accessing vars
7 Patrick Hemmer reported that a simple tcp rule involving a variable like
8 this is enough to crash haproxy :
12 tcp-request session set-var(txn.foo) src
14 The tests on the variables scopes is not strict enough, it needs to always
15 verify if the stream is valid when accessing a req/res/txn variable. This
16 patch does this by adding a new get_vars() function which does the job
17 instead of open-coding all the lookups everywhere.
19 It must be backported to all versions supporting set-var and
20 "tcp-request session" so at least 1.9 and 1.8.
22 (cherry picked from commit f37b140b06b9963dea8adaf5e13b5b57cd219c74)
24 Signed-off-by: Willy Tarreau <w@1wt.eu>
25 (cherry picked from commit 5dcf8515592602ed0d962e365cbb74a3646727c1)
26 Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
28 diff --git a/src/vars.c b/src/vars.c
29 index 566ead6e..c86f612f 100644
32 @@ -34,6 +34,25 @@ static unsigned int var_reqres_limit = 0;
34 __decl_hathreads(HA_RWLOCK_T var_names_rwlock);
36 +/* returns the struct vars pointer for a session, stream and scope, or NULL if
37 + * it does not exist.
39 +static inline struct vars *get_vars(struct session *sess, struct stream *strm, enum vars_scope scope)
43 + return &global.vars;
47 + return strm ? &strm->vars_txn : NULL;
51 + return strm ? &strm->vars_reqres : NULL;
55 /* This function adds or remove memory size from the accounting. The inner
56 * pointers may be null when setting the outer ones only.
58 @@ -42,10 +61,12 @@ static void var_accounting_diff(struct vars *vars, struct session *sess, struct
59 switch (vars->scope) {
62 - HA_ATOMIC_ADD(&strm->vars_reqres.size, size);
64 + HA_ATOMIC_ADD(&strm->vars_reqres.size, size);
67 - HA_ATOMIC_ADD(&strm->vars_txn.size, size);
69 + HA_ATOMIC_ADD(&strm->vars_txn.size, size);
72 HA_ATOMIC_ADD(&sess->vars.size, size);
73 @@ -68,11 +89,11 @@ static int var_accounting_add(struct vars *vars, struct session *sess, struct st
74 switch (vars->scope) {
77 - if (var_reqres_limit && strm->vars_reqres.size + size > var_reqres_limit)
78 + if (var_reqres_limit && strm && strm->vars_reqres.size + size > var_reqres_limit)
82 - if (var_txn_limit && strm->vars_txn.size + size > var_txn_limit)
83 + if (var_txn_limit && strm && strm->vars_txn.size + size > var_txn_limit)
87 @@ -285,27 +306,8 @@ static int smp_fetch_var(const struct arg *args, struct sample *smp, const char
90 /* Check the availibity of the variable. */
91 - switch (var_desc->scope) {
93 - vars = &global.vars;
96 - vars = &smp->sess->vars;
101 - vars = &smp->strm->vars_txn;
108 - vars = &smp->strm->vars_reqres;
111 - if (vars->scope != var_desc->scope)
112 + vars = get_vars(smp->sess, smp->strm, var_desc->scope);
113 + if (!vars || vars->scope != var_desc->scope)
116 HA_RWLOCK_RDLOCK(VARS_LOCK, &vars->rwlock);
117 @@ -423,15 +425,8 @@ static inline int sample_store_stream(const char *name, enum vars_scope scope, s
122 - case SCOPE_PROC: vars = &global.vars; break;
123 - case SCOPE_SESS: vars = &smp->sess->vars; break;
124 - case SCOPE_TXN: vars = &smp->strm->vars_txn; break;
127 - default: vars = &smp->strm->vars_reqres; break;
129 - if (vars->scope != scope)
130 + vars = get_vars(smp->sess, smp->strm, scope);
131 + if (!vars || vars->scope != scope)
134 HA_RWLOCK_WRLOCK(VARS_LOCK, &vars->rwlock);
135 @@ -447,15 +442,8 @@ static inline int sample_clear_stream(const char *name, enum vars_scope scope, s
137 unsigned int size = 0;
140 - case SCOPE_PROC: vars = &global.vars; break;
141 - case SCOPE_SESS: vars = &smp->sess->vars; break;
142 - case SCOPE_TXN: vars = &smp->strm->vars_txn; break;
145 - default: vars = &smp->strm->vars_reqres; break;
147 - if (vars->scope != scope)
148 + vars = get_vars(smp->sess, smp->strm, scope);
149 + if (!vars || vars->scope != scope)
152 /* Look for existing variable name. */
153 @@ -586,17 +574,8 @@ int vars_get_by_name(const char *name, size_t len, struct sample *smp)
156 /* Select "vars" pool according with the scope. */
158 - case SCOPE_PROC: vars = &global.vars; break;
159 - case SCOPE_SESS: vars = &smp->sess->vars; break;
160 - case SCOPE_TXN: vars = &smp->strm->vars_txn; break;
163 - default: vars = &smp->strm->vars_reqres; break;
166 - /* Check if the scope is avalaible a this point of processing. */
167 - if (vars->scope != scope)
168 + vars = get_vars(smp->sess, smp->strm, scope);
169 + if (!vars || vars->scope != scope)
172 /* Get the variable entry. */
173 @@ -620,17 +599,10 @@ int vars_get_by_desc(const struct var_desc *var_desc, struct sample *smp)
176 /* Select "vars" pool according with the scope. */
177 - switch (var_desc->scope) {
178 - case SCOPE_PROC: vars = &global.vars; break;
179 - case SCOPE_SESS: vars = &smp->sess->vars; break;
180 - case SCOPE_TXN: vars = &smp->strm->vars_txn; break;
183 - default: vars = &smp->strm->vars_reqres; break;
185 + vars = get_vars(smp->sess, smp->strm, var_desc->scope);
187 - /* Check if the scope is avalaible a this point of processing. */
188 - if (vars->scope != var_desc->scope)
189 + /* Check if the scope is available a this point of processing. */
190 + if (!vars || vars->scope != var_desc->scope)
193 /* Get the variable entry. */