5312464a8decc2f305e3460901dc3d23f0237da1
[feed/packages.git] /
1 commit 47133bd99225554519c1d32293e0e5c3db83db30
2 Author: Willy Tarreau <w@1wt.eu>
3 Date: Tue Jun 4 16:27:36 2019 +0200
4
5 BUG/MEDIUM: vars: make sure the scope is always valid when accessing vars
6
7 Patrick Hemmer reported that a simple tcp rule involving a variable like
8 this is enough to crash haproxy :
9
10 frontend foo
11 bind :8001
12 tcp-request session set-var(txn.foo) src
13
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.
18
19 It must be backported to all versions supporting set-var and
20 "tcp-request session" so at least 1.9 and 1.8.
21
22 (cherry picked from commit f37b140b06b9963dea8adaf5e13b5b57cd219c74)
23 [wt: s/_HA_/HA_/]
24 Signed-off-by: Willy Tarreau <w@1wt.eu>
25 (cherry picked from commit 5dcf8515592602ed0d962e365cbb74a3646727c1)
26 Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
27
28 diff --git a/src/vars.c b/src/vars.c
29 index 566ead6e..c86f612f 100644
30 --- a/src/vars.c
31 +++ b/src/vars.c
32 @@ -34,6 +34,25 @@ static unsigned int var_reqres_limit = 0;
33
34 __decl_hathreads(HA_RWLOCK_T var_names_rwlock);
35
36 +/* returns the struct vars pointer for a session, stream and scope, or NULL if
37 + * it does not exist.
38 + */
39 +static inline struct vars *get_vars(struct session *sess, struct stream *strm, enum vars_scope scope)
40 +{
41 + switch (scope) {
42 + case SCOPE_PROC:
43 + return &global.vars;
44 + case SCOPE_SESS:
45 + return &sess->vars;
46 + case SCOPE_TXN:
47 + return strm ? &strm->vars_txn : NULL;
48 + case SCOPE_REQ:
49 + case SCOPE_RES:
50 + default:
51 + return strm ? &strm->vars_reqres : NULL;
52 + }
53 +}
54 +
55 /* This function adds or remove memory size from the accounting. The inner
56 * pointers may be null when setting the outer ones only.
57 */
58 @@ -42,10 +61,12 @@ static void var_accounting_diff(struct vars *vars, struct session *sess, struct
59 switch (vars->scope) {
60 case SCOPE_REQ:
61 case SCOPE_RES:
62 - HA_ATOMIC_ADD(&strm->vars_reqres.size, size);
63 + if (strm)
64 + HA_ATOMIC_ADD(&strm->vars_reqres.size, size);
65 /* fall through */
66 case SCOPE_TXN:
67 - HA_ATOMIC_ADD(&strm->vars_txn.size, size);
68 + if (strm)
69 + HA_ATOMIC_ADD(&strm->vars_txn.size, size);
70 /* fall through */
71 case SCOPE_SESS:
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) {
75 case SCOPE_REQ:
76 case SCOPE_RES:
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)
79 return 0;
80 /* fall through */
81 case SCOPE_TXN:
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)
84 return 0;
85 /* fall through */
86 case SCOPE_SESS:
87 @@ -285,27 +306,8 @@ static int smp_fetch_var(const struct arg *args, struct sample *smp, const char
88 struct vars *vars;
89
90 /* Check the availibity of the variable. */
91 - switch (var_desc->scope) {
92 - case SCOPE_PROC:
93 - vars = &global.vars;
94 - break;
95 - case SCOPE_SESS:
96 - vars = &smp->sess->vars;
97 - break;
98 - case SCOPE_TXN:
99 - if (!smp->strm)
100 - return 0;
101 - vars = &smp->strm->vars_txn;
102 - break;
103 - case SCOPE_REQ:
104 - case SCOPE_RES:
105 - default:
106 - if (!smp->strm)
107 - return 0;
108 - vars = &smp->strm->vars_reqres;
109 - break;
110 - }
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)
114 return 0;
115
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
118 struct vars *vars;
119 int ret;
120
121 - switch (scope) {
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;
125 - case SCOPE_REQ:
126 - case SCOPE_RES:
127 - default: vars = &smp->strm->vars_reqres; break;
128 - }
129 - if (vars->scope != scope)
130 + vars = get_vars(smp->sess, smp->strm, scope);
131 + if (!vars || vars->scope != scope)
132 return 0;
133
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
136 struct var *var;
137 unsigned int size = 0;
138
139 - switch (scope) {
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;
143 - case SCOPE_REQ:
144 - case SCOPE_RES:
145 - default: vars = &smp->strm->vars_reqres; break;
146 - }
147 - if (vars->scope != scope)
148 + vars = get_vars(smp->sess, smp->strm, scope);
149 + if (!vars || vars->scope != scope)
150 return 0;
151
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)
154 return 0;
155
156 /* Select "vars" pool according with the scope. */
157 - switch (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;
161 - case SCOPE_REQ:
162 - case SCOPE_RES:
163 - default: vars = &smp->strm->vars_reqres; break;
164 - }
165 -
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)
170 return 0;
171
172 /* Get the variable entry. */
173 @@ -620,17 +599,10 @@ int vars_get_by_desc(const struct var_desc *var_desc, struct sample *smp)
174 struct var *var;
175
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;
181 - case SCOPE_REQ:
182 - case SCOPE_RES:
183 - default: vars = &smp->strm->vars_reqres; break;
184 - }
185 + vars = get_vars(smp->sess, smp->strm, var_desc->scope);
186
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)
191 return 0;
192
193 /* Get the variable entry. */