Home > Back-end >  Is reading a property from an assignment expression UB in C
Is reading a property from an assignment expression UB in C

Time:12-25

I have the following code snippet:

_nhp_array_char_t intpd_str1; 
intpd_str1.length = snprintf(NULL, 0, "You\'re %.*s!", (_nhp_var_str = _nhp_var_you->to_array_char->_nhp_this_anon(_nhp_var_you->to_array_char)).length, _nhp_var_str.buffer);
intpd_str1.buffer = _nhp_malloc(intpd_str1.length   1); 
intpd_str1.responsible_destroyer = NULL; 
snprintf(intpd_str1.buffer, intpd_str1.length   1, "You\'re %.*s!", _nhp_var_str.length, _nhp_var_str.buffer);

Calling _nhp_var_you->to_array_char->_nhp_this_anon(_nhp_var_you->to_array_char) should, and does, return a struct with length 22, and a valid value for buffer.

The expression intpd_str1.length = snprintf... evaluates to 14, however. However, in GDB, running the assignment expression beforehand, then reading _nhp_var_str.length returns the correct value of 30.

I suspect that there must be some form of undefined-behavior, though I haven't been able to confirm it, even after searching google and SO. I think, for some reason, when evaluating (_nhp_var_str = _nhp_var_you->to_array_char->_nhp_this_anon(_nhp_var_you->to_array_char)).length, it decides to produce the result of _nhp_var_str.length instead without assigning a value to _nhp_var_str.

FYI, the code looks messy because it's transpiled; as far as coding style goes, there's not much I can do to actually change it.

CodePudding user response:

The evaluation of function call arguments is unsequenced. In snprintf(NULL, 0, "You\'re %.*s!", (_nhp_var_str = _nhp_var_you->to_array_char->_nhp_this_anon(_nhp_var_you->to_array_char)).length, _nhp_var_str.buffer);, the fourth and fifth arguments are:

  • (_nhp_var_str = _nhp_var_you->to_array_char->_nhp_this_anon(_nhp_var_you->to_array_char)).length, and
  • _nhp_var_str.buffer.

The fourth argument modifies the value of _nhp_var_str (as a side effect of the assignment), including its buffer member, and the fifth argument uses the value of _nhp_var_str.buffer. This violates C 2018 6.5 2:

If a side effect on a scalar object is unsequenced relative to either a different side effect on the same scalar object or a value computation using the value of the same scalar object, the behavior is undefined…

Note that the scalar object in question is _nhp_var_str.buffer, which I presume is a pointer, rather than the whole _nhp_var_str structure. (A structure is an aggregate, not a scalar.)

The behavior of the code is fundamentally not defined by the C standard. Even if we disregard 6.5 2, a C implementation may evaluate the fifth argument before it evaluates the fourth, and this results in using a value of _nhp_var_str.buffer before it has been set by the assignment in the fourth argument. So this output from the transpiler is broken; the transpiler is defective. Either it must be fixed or the code must be corrected after it is generated by the transpiler.

  • Related