diff options
author | Mark Pulford <mark@kyne.com.au> | 2012-01-12 19:27:18 +1030 |
---|---|---|
committer | Mark Pulford <mark@kyne.com.au> | 2012-03-04 18:54:34 +1030 |
commit | 5e36beccab38fa40b2c324c9339bbd5f1a3f24e3 (patch) | |
tree | 28ee3145a3c4e472c6b5bcdae574444a95aba479 | |
parent | d4f438965e41660bd0beda772ba3d3b96a492571 (diff) | |
download | lua-cjson-5e36beccab38fa40b2c324c9339bbd5f1a3f24e3.tar.gz lua-cjson-5e36beccab38fa40b2c324c9339bbd5f1a3f24e3.tar.bz2 lua-cjson-5e36beccab38fa40b2c324c9339bbd5f1a3f24e3.zip |
Add configurable decode nesting limit
Lua 5.2 is able to extend the Lua stack much further than earlier
versions. Recent testing shows it is possible for Lua CJSON to hit the
process stack limit and segfault. Add a configurable JSON object/array
nesting limit to prevent running out of process stack space.
The current limit is 20 (same as encode).
Add decode_max_depth() configuration function.
Diffstat (limited to '')
-rw-r--r-- | lua_cjson.c | 98 | ||||
-rw-r--r-- | manual.txt | 26 |
2 files changed, 89 insertions, 35 deletions
diff --git a/lua_cjson.c b/lua_cjson.c index 344cb43..bdfa7d4 100644 --- a/lua_cjson.c +++ b/lua_cjson.c | |||
@@ -39,6 +39,7 @@ | |||
39 | #include <assert.h> | 39 | #include <assert.h> |
40 | #include <string.h> | 40 | #include <string.h> |
41 | #include <math.h> | 41 | #include <math.h> |
42 | #include <limits.h> | ||
42 | #include <lua.h> | 43 | #include <lua.h> |
43 | #include <lauxlib.h> | 44 | #include <lauxlib.h> |
44 | 45 | ||
@@ -61,7 +62,8 @@ | |||
61 | #define DEFAULT_SPARSE_CONVERT 0 | 62 | #define DEFAULT_SPARSE_CONVERT 0 |
62 | #define DEFAULT_SPARSE_RATIO 2 | 63 | #define DEFAULT_SPARSE_RATIO 2 |
63 | #define DEFAULT_SPARSE_SAFE 10 | 64 | #define DEFAULT_SPARSE_SAFE 10 |
64 | #define DEFAULT_MAX_DEPTH 20 | 65 | #define DEFAULT_ENCODE_MAX_DEPTH 20 |
66 | #define DEFAULT_DECODE_MAX_DEPTH 20 | ||
65 | #define DEFAULT_ENCODE_REFUSE_BADNUM 1 | 67 | #define DEFAULT_ENCODE_REFUSE_BADNUM 1 |
66 | #define DEFAULT_DECODE_REFUSE_BADNUM 0 | 68 | #define DEFAULT_DECODE_REFUSE_BADNUM 0 |
67 | #define DEFAULT_ENCODE_KEEP_BUFFER 1 | 69 | #define DEFAULT_ENCODE_KEEP_BUFFER 1 |
@@ -120,9 +122,11 @@ typedef struct { | |||
120 | int encode_sparse_safe; | 122 | int encode_sparse_safe; |
121 | int encode_max_depth; | 123 | int encode_max_depth; |
122 | int encode_refuse_badnum; | 124 | int encode_refuse_badnum; |
123 | int decode_refuse_badnum; | ||
124 | int encode_keep_buffer; | ||
125 | int encode_number_precision; | 125 | int encode_number_precision; |
126 | int encode_keep_buffer; | ||
127 | |||
128 | int decode_refuse_badnum; | ||
129 | int decode_max_depth; | ||
126 | } json_config_t; | 130 | } json_config_t; |
127 | 131 | ||
128 | typedef struct { | 132 | typedef struct { |
@@ -130,6 +134,7 @@ typedef struct { | |||
130 | const char *ptr; | 134 | const char *ptr; |
131 | strbuf_t *tmp; /* Temporary storage for strings */ | 135 | strbuf_t *tmp; /* Temporary storage for strings */ |
132 | json_config_t *cfg; | 136 | json_config_t *cfg; |
137 | int current_depth; | ||
133 | } json_parse_t; | 138 | } json_parse_t; |
134 | 139 | ||
135 | typedef struct { | 140 | typedef struct { |
@@ -234,46 +239,50 @@ static int json_cfg_encode_sparse_array(lua_State *l) | |||
234 | return 3; | 239 | return 3; |
235 | } | 240 | } |
236 | 241 | ||
237 | /* Configures the maximum number of nested arrays/objects allowed when | 242 | /* Process integer options for configuration functions */ |
238 | * encoding */ | 243 | static int json_integer_option(lua_State *l, int *setting, int min, int max) |
239 | static int json_cfg_encode_max_depth(lua_State *l) | ||
240 | { | 244 | { |
241 | json_config_t *cfg; | 245 | char errmsg[64]; |
242 | int depth; | 246 | int value; |
243 | 247 | ||
244 | json_verify_arg_count(l, 1); | 248 | json_verify_arg_count(l, 1); |
245 | cfg = json_fetch_config(l); | ||
246 | 249 | ||
247 | if (lua_gettop(l)) { | 250 | if (lua_gettop(l)) { |
248 | depth = luaL_checkinteger(l, 1); | 251 | value = luaL_checkinteger(l, 1); |
249 | luaL_argcheck(l, depth > 0, 1, "expected positive integer"); | 252 | snprintf(errmsg, sizeof(errmsg), "expected integer between %d and %d", min, max); |
250 | cfg->encode_max_depth = depth; | 253 | luaL_argcheck(l, min <= value && value <= max, 1, errmsg); |
254 | *setting = value; | ||
251 | } | 255 | } |
252 | 256 | ||
253 | lua_pushinteger(l, cfg->encode_max_depth); | 257 | lua_pushinteger(l, *setting); |
254 | 258 | ||
255 | return 1; | 259 | return 1; |
256 | } | 260 | } |
257 | 261 | ||
258 | /* Configures number precision when converting doubles to text */ | 262 | /* Configures the maximum number of nested arrays/objects allowed when |
259 | static int json_cfg_encode_number_precision(lua_State *l) | 263 | * encoding */ |
264 | static int json_cfg_encode_max_depth(lua_State *l) | ||
260 | { | 265 | { |
261 | json_config_t *cfg; | 266 | json_config_t *cfg = json_fetch_config(l); |
262 | int precision; | ||
263 | 267 | ||
264 | json_verify_arg_count(l, 1); | 268 | return json_integer_option(l, &cfg->encode_max_depth, 1, INT_MAX); |
265 | cfg = json_fetch_config(l); | 269 | } |
266 | 270 | ||
267 | if (lua_gettop(l)) { | 271 | /* Configures the maximum number of nested arrays/objects allowed when |
268 | precision = luaL_checkinteger(l, 1); | 272 | * encoding */ |
269 | luaL_argcheck(l, 1 <= precision && precision <= 14, 1, | 273 | static int json_cfg_decode_max_depth(lua_State *l) |
270 | "expected integer between 1 and 14"); | 274 | { |
271 | cfg->encode_number_precision = precision; | 275 | json_config_t *cfg = json_fetch_config(l); |
272 | } | ||
273 | 276 | ||
274 | lua_pushinteger(l, cfg->encode_number_precision); | 277 | return json_integer_option(l, &cfg->decode_max_depth, 1, INT_MAX); |
278 | } | ||
275 | 279 | ||
276 | return 1; | 280 | /* Configures number precision when converting doubles to text */ |
281 | static int json_cfg_encode_number_precision(lua_State *l) | ||
282 | { | ||
283 | json_config_t *cfg = json_fetch_config(l); | ||
284 | |||
285 | return json_integer_option(l, &cfg->encode_number_precision, 1, 14); | ||
277 | } | 286 | } |
278 | 287 | ||
279 | /* Configures JSON encoding buffer persistence */ | 288 | /* Configures JSON encoding buffer persistence */ |
@@ -387,7 +396,8 @@ static void json_create_config(lua_State *l) | |||
387 | cfg->encode_sparse_convert = DEFAULT_SPARSE_CONVERT; | 396 | cfg->encode_sparse_convert = DEFAULT_SPARSE_CONVERT; |
388 | cfg->encode_sparse_ratio = DEFAULT_SPARSE_RATIO; | 397 | cfg->encode_sparse_ratio = DEFAULT_SPARSE_RATIO; |
389 | cfg->encode_sparse_safe = DEFAULT_SPARSE_SAFE; | 398 | cfg->encode_sparse_safe = DEFAULT_SPARSE_SAFE; |
390 | cfg->encode_max_depth = DEFAULT_MAX_DEPTH; | 399 | cfg->encode_max_depth = DEFAULT_ENCODE_MAX_DEPTH; |
400 | cfg->decode_max_depth = DEFAULT_DECODE_MAX_DEPTH; | ||
391 | cfg->encode_refuse_badnum = DEFAULT_ENCODE_REFUSE_BADNUM; | 401 | cfg->encode_refuse_badnum = DEFAULT_ENCODE_REFUSE_BADNUM; |
392 | cfg->decode_refuse_badnum = DEFAULT_DECODE_REFUSE_BADNUM; | 402 | cfg->decode_refuse_badnum = DEFAULT_DECODE_REFUSE_BADNUM; |
393 | cfg->encode_keep_buffer = DEFAULT_ENCODE_KEEP_BUFFER; | 403 | cfg->encode_keep_buffer = DEFAULT_ENCODE_KEEP_BUFFER; |
@@ -1094,10 +1104,19 @@ static void json_throw_parse_error(lua_State *l, json_parse_t *json, | |||
1094 | exp, found, token->index + 1); | 1104 | exp, found, token->index + 1); |
1095 | } | 1105 | } |
1096 | 1106 | ||
1097 | static void json_decode_checkstack(lua_State *l, json_parse_t *json, int n) | 1107 | static inline void json_decode_ascend(json_parse_t *json) |
1098 | { | 1108 | { |
1099 | if (lua_checkstack(l, n)) | 1109 | json->current_depth--; |
1110 | } | ||
1111 | |||
1112 | static void json_decode_descend(lua_State *l, json_parse_t *json, int slots) | ||
1113 | { | ||
1114 | json->current_depth++; | ||
1115 | |||
1116 | if (json->current_depth <= json->cfg->decode_max_depth && | ||
1117 | lua_checkstack(l, slots)) { | ||
1100 | return; | 1118 | return; |
1119 | } | ||
1101 | 1120 | ||
1102 | strbuf_free(json->tmp); | 1121 | strbuf_free(json->tmp); |
1103 | luaL_error(l, "Too many nested data structures"); | 1122 | luaL_error(l, "Too many nested data structures"); |
@@ -1109,7 +1128,7 @@ static void json_parse_object_context(lua_State *l, json_parse_t *json) | |||
1109 | 1128 | ||
1110 | /* 3 slots required: | 1129 | /* 3 slots required: |
1111 | * .., table, key, value */ | 1130 | * .., table, key, value */ |
1112 | json_decode_checkstack(l, json, 3); | 1131 | json_decode_descend(l, json, 3); |
1113 | 1132 | ||
1114 | lua_newtable(l); | 1133 | lua_newtable(l); |
1115 | 1134 | ||
@@ -1117,6 +1136,7 @@ static void json_parse_object_context(lua_State *l, json_parse_t *json) | |||
1117 | 1136 | ||
1118 | /* Handle empty objects */ | 1137 | /* Handle empty objects */ |
1119 | if (token.type == T_OBJ_END) { | 1138 | if (token.type == T_OBJ_END) { |
1139 | json_decode_ascend(json); | ||
1120 | return; | 1140 | return; |
1121 | } | 1141 | } |
1122 | 1142 | ||
@@ -1140,8 +1160,10 @@ static void json_parse_object_context(lua_State *l, json_parse_t *json) | |||
1140 | 1160 | ||
1141 | json_next_token(json, &token); | 1161 | json_next_token(json, &token); |
1142 | 1162 | ||
1143 | if (token.type == T_OBJ_END) | 1163 | if (token.type == T_OBJ_END) { |
1164 | json_decode_ascend(json); | ||
1144 | return; | 1165 | return; |
1166 | } | ||
1145 | 1167 | ||
1146 | if (token.type != T_COMMA) | 1168 | if (token.type != T_COMMA) |
1147 | json_throw_parse_error(l, json, "comma or object end", &token); | 1169 | json_throw_parse_error(l, json, "comma or object end", &token); |
@@ -1158,15 +1180,17 @@ static void json_parse_array_context(lua_State *l, json_parse_t *json) | |||
1158 | 1180 | ||
1159 | /* 2 slots required: | 1181 | /* 2 slots required: |
1160 | * .., table, value */ | 1182 | * .., table, value */ |
1161 | json_decode_checkstack(l, json, 2); | 1183 | json_decode_descend(l, json, 2); |
1162 | 1184 | ||
1163 | lua_newtable(l); | 1185 | lua_newtable(l); |
1164 | 1186 | ||
1165 | json_next_token(json, &token); | 1187 | json_next_token(json, &token); |
1166 | 1188 | ||
1167 | /* Handle empty arrays */ | 1189 | /* Handle empty arrays */ |
1168 | if (token.type == T_ARR_END) | 1190 | if (token.type == T_ARR_END) { |
1191 | json_decode_ascend(json); | ||
1169 | return; | 1192 | return; |
1193 | } | ||
1170 | 1194 | ||
1171 | for (i = 1; ; i++) { | 1195 | for (i = 1; ; i++) { |
1172 | json_process_value(l, json, &token); | 1196 | json_process_value(l, json, &token); |
@@ -1174,8 +1198,10 @@ static void json_parse_array_context(lua_State *l, json_parse_t *json) | |||
1174 | 1198 | ||
1175 | json_next_token(json, &token); | 1199 | json_next_token(json, &token); |
1176 | 1200 | ||
1177 | if (token.type == T_ARR_END) | 1201 | if (token.type == T_ARR_END) { |
1202 | json_decode_ascend(json); | ||
1178 | return; | 1203 | return; |
1204 | } | ||
1179 | 1205 | ||
1180 | if (token.type != T_COMMA) | 1206 | if (token.type != T_COMMA) |
1181 | json_throw_parse_error(l, json, "comma or array end", &token); | 1207 | json_throw_parse_error(l, json, "comma or array end", &token); |
@@ -1221,6 +1247,7 @@ static void lua_json_decode(lua_State *l, const char *json_text, int json_len) | |||
1221 | json_token_t token; | 1247 | json_token_t token; |
1222 | 1248 | ||
1223 | json.cfg = json_fetch_config(l); | 1249 | json.cfg = json_fetch_config(l); |
1250 | json.current_depth = 0; | ||
1224 | json.data = json_text; | 1251 | json.data = json_text; |
1225 | json.ptr = json.data; | 1252 | json.ptr = json.data; |
1226 | 1253 | ||
@@ -1292,6 +1319,7 @@ static int lua_cjson_new(lua_State *l) | |||
1292 | { "decode", json_decode }, | 1319 | { "decode", json_decode }, |
1293 | { "encode_sparse_array", json_cfg_encode_sparse_array }, | 1320 | { "encode_sparse_array", json_cfg_encode_sparse_array }, |
1294 | { "encode_max_depth", json_cfg_encode_max_depth }, | 1321 | { "encode_max_depth", json_cfg_encode_max_depth }, |
1322 | { "decode_max_depth", json_cfg_decode_max_depth }, | ||
1295 | { "encode_number_precision", json_cfg_encode_number_precision }, | 1323 | { "encode_number_precision", json_cfg_encode_number_precision }, |
1296 | { "encode_keep_buffer", json_cfg_encode_keep_buffer }, | 1324 | { "encode_keep_buffer", json_cfg_encode_keep_buffer }, |
1297 | { "refuse_invalid_numbers", json_cfg_refuse_invalid_numbers }, | 1325 | { "refuse_invalid_numbers", json_cfg_refuse_invalid_numbers }, |
@@ -158,6 +158,7 @@ setting = cjson.refuse_invalid_numbers([setting]) | |||
158 | depth = cjson.encode_max_depth([depth]) | 158 | depth = cjson.encode_max_depth([depth]) |
159 | convert, ratio, safe = cjson.encode_sparse_array([convert[, ratio[, safe]]]) | 159 | convert, ratio, safe = cjson.encode_sparse_array([convert[, ratio[, safe]]]) |
160 | keep = cjson.encode_keep_buffer([keep]) | 160 | keep = cjson.encode_keep_buffer([keep]) |
161 | depth = cjson.decode_max_depth([depth]) | ||
161 | ------------ | 162 | ------------ |
162 | 163 | ||
163 | 164 | ||
@@ -239,6 +240,31 @@ numeric key will be stored as a Lua +string+. Any code assuming type | |||
239 | +number+ may break. | 240 | +number+ may break. |
240 | 241 | ||
241 | 242 | ||
243 | [[decode_max_depth]] | ||
244 | decode_max_depth | ||
245 | ~~~~~~~~~~~~~~~~ | ||
246 | |||
247 | [source,lua] | ||
248 | ------------ | ||
249 | depth = cjson.decode_max_depth([depth]) | ||
250 | -- "depth" must be a positive integer | ||
251 | ------------ | ||
252 | |||
253 | By default, Lua CJSON will reject JSON with arrays and/or objects | ||
254 | nested more than 20 deep. | ||
255 | |||
256 | This setting is only changed when an argument is provided. The current | ||
257 | setting is always returned. | ||
258 | |||
259 | When the maximum array/object depth is exceeded Lua CJSON will throw | ||
260 | an error. An error may be thrown before the depth limit is hit if Lua | ||
261 | is unable to allocate more objects on the Lua stack. | ||
262 | |||
263 | This check prevents unnecessarily complicated JSON from slowing down | ||
264 | the application, or crashing the application due to lack of process | ||
265 | stack space. | ||
266 | |||
267 | |||
242 | encode | 268 | encode |
243 | ~~~~~~ | 269 | ~~~~~~ |
244 | 270 | ||