From: John Darrington Date: Sat, 27 Apr 2013 05:21:24 +0000 (+0200) Subject: Fixed a crash when RECODE attempted to increase the length of a string beyond the... X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=49b3e593c078b40d7c72379b4f8622a1ea8d7ea1;p=pspp Fixed a crash when RECODE attempted to increase the length of a string beyond the width of the variable. Reported-by: Cristoph Amthor --- diff --git a/doc/transformation.texi b/doc/transformation.texi index 5ea809e54f..dfacfcdaed 100644 --- a/doc/transformation.texi +++ b/doc/transformation.texi @@ -579,13 +579,17 @@ The behaviour of the command is slightly different depending on whether it appears or not. If @samp{INTO @var{dest_vars}} does not appear, then values will be recoded -``in place´´. This means that the recoded values are written back to the +``in place´´. +This means that the recoded values are written back to the source variables from whence the original values came. In this case, the @var{dest_value} for every mapping must imply a value which has the same type as the @var{src_value}. For example, if the source value is a string value, it is not permissible for @var{dest_value} to be @samp{SYSMIS} or another forms which implies a numeric result. +It is also not permissible for @var{dest_value} to be longer than the width +of the source variable. + The following example two numeric variables @var{x} and @var{y} are recoded in place. Zero is recoded to 99, the values 1 to 10 inclusive are unchanged, diff --git a/src/language/xforms/recode.c b/src/language/xforms/recode.c index 44e5b85133..2849ea32d1 100644 --- a/src/language/xforms/recode.c +++ b/src/language/xforms/recode.c @@ -125,7 +125,7 @@ static void set_map_out_num (struct map_out *, double); static void set_map_out_str (struct map_out *, struct pool *, struct substring); -static void enlarge_dst_widths (struct recode_trns *); +static bool enlarge_dst_widths (struct recode_trns *); static void create_dst_vars (struct recode_trns *, struct dictionary *); static trns_proc_func recode_trns_proc; @@ -157,7 +157,12 @@ cmd_recode (struct lexer *lexer, struct dataset *ds) /* Ensure that all the output strings are at least as wide as the widest destination variable. */ if (trns->dst_type == VAL_STRING) - enlarge_dst_widths (trns); + { + if ( ! enlarge_dst_widths (trns)) + { + return CMD_FAILURE; + } + } /* Create destination variables, if needed. This must be the final step; otherwise we'd have to @@ -528,26 +533,46 @@ parse_dst_vars (struct lexer *lexer, struct recode_trns *trns, /* Ensures that all the output values in TRNS are as wide as the widest destination variable. */ -static void +static bool enlarge_dst_widths (struct recode_trns *trns) { size_t i; - + const struct variable *narrow_var = NULL; + int min_dst_width = INT_MAX; trns->max_dst_width = 0; + for (i = 0; i < trns->var_cnt; i++) { const struct variable *v = trns->dst_vars[i]; if (var_get_width (v) > trns->max_dst_width) trns->max_dst_width = var_get_width (v); + + if (var_get_width (v) < min_dst_width) + { + min_dst_width = var_get_width (v); + narrow_var = v; + } } for (i = 0; i < trns->map_cnt; i++) { struct map_out *out = &trns->mappings[i].out; if (!out->copy_input) - value_resize_pool (trns->pool, &out->value, - out->width, trns->max_dst_width); + { + if (out->width > min_dst_width) + { + msg (ME, + _("Cannot recode because the variable %s would require a width of %d bytes or greater, but it has a width of only %d bytes."), + var_get_name (narrow_var), out->width, trns->max_dst_width); + return false; + } + + value_resize_pool (trns->pool, &out->value, + out->width, trns->max_dst_width); + } } + + return true; } /* Creates destination variables that don't already exist. */ diff --git a/tests/language/xforms/recode.at b/tests/language/xforms/recode.at index aa50054e09..ae434c657a 100644 --- a/tests/language/xforms/recode.at +++ b/tests/language/xforms/recode.at @@ -294,3 +294,40 @@ A,B,A1 3,4,1 ]) AT_CLEANUP + + + +AT_SETUP([RECODE increased string widths]) + +AT_DATA([recode.sps],[dnl +data list list /x (a1) y (a8) z *. +begin data. +a a 2 +a two 2 +b three 2 +c b 2 +end data. + +recode x y ("a" = "first") . + +list. +]) + +AT_CHECK([pspp -O format=csv recode.sps], [1], [dnl +Table: Reading free-form data from INLINE. +Variable,Format +x,A1 +y,A8 +z,F8.0 + +"error: Cannot recode in place, because the variable x would require a width of 5 bytes or greater, but it has a width of only 8 bytes." + +Table: Data List +x,y,z +a,a ,2.00 +a,two ,2.00 +b,three ,2.00 +c,b ,2.00 +]) + +AT_CLEANUP