c++ STL map::operator[] done on an entry being deleted
Asked Answered
B

1

4
std::map<int,int> bar;

int foo(int key)
{
  bar.erase(key);
  return 1;
}    

int main()
{
  bar[0] = foo(0);
  return 0;
}

This code compiled with GCC 4.8 segs fault when checking memory usage with electric fence.

LD_PRELOAD=libefence.so.0.0 ./a.out

The problem comes from the fact that the compiler generates a code that starts to allocate a new entry in the map, then executes foo() to get the value to put into bar[0]. While running foo(), the entry gets destroyed and the code finally ends by writing in non-allocated memory.

Does the way the operations are ordered depend on the compiler implementation, or is it specified by the C++ current standard?

Bot answered 14/10, 2015 at 17:33 Comment(5)
See also stackoverflow.com/a/4183735.Shatzer
Look into sequencing and sequence points. It's undefined whether bar[0] will be evaluated before foo(0) if it is (and it looks like it is for you) then you're in big trouble.Choleric
@JasonMc92 - There isn't a single occurrence of either new or delete in OP!Noah
@HappyGreenKidNaps, argh, I misread "erase" as "delete", Darned dyslexia. Sorry about that. Slumps down and takes a long drink of coffee.Lothair
You know, I have to agree with @KerrekSB here. Curiosity's great and it's nice to know why doing something that looks iffy and did not work won't work, but sometimes the simple solution's the best.Codding
H
5

The standard (§1.9 15) specifies that the evaluation of the two operands to a binary operator is unsequenced (unless in some specific cases):

Except where noted, evaluations of operands of individual operators and of subexpressions of individual expressions are unsequenced.

This means that it does not mandate that one side of the assignment operation is evaluated before the other, and in fact, it is undefined behavior to depend on the order of these unsequenced operations.

This is also generally true for the order of evaluation of function arguments.

You need to break your assignment in two:

int result = foo(0);
bar[0] = result;
Hindward answered 14/10, 2015 at 17:39 Comment(4)
"and in fact, it is undefined behavior to depend on the order of the operations" -- It's not, generally. The order of the two function calls is unspecified, but that only means you can't reliably tell which function will be called first. It just so happens that in the OP's case, depending on the order, a dangling reference will be accessed. That renders the behaviour undefined, not the mere fact of depending on a specific order of operations. Even if I want printf("Hello, ") + printf("world!") to print Hello, world!, I am guaranteed to either the intended output or world!Hello, .Inhabit
@hvd, yes, I meant that it's UB to depend on the order of unsequenced operations.Hindward
Oh, oops, terminology mix-up. That's "indeterminately sequenced", not "unsequenced", both in my example and in the OP's code, because of the function calls. Do you still think it's UB then?Inhabit
Yes, it says "Except where noted", and also contains "Every evaluation in the calling function (including other function calls) that is not otherwise specifically sequenced before or after the execution of the body of the called function is indeterminately sequenced with respect to the execution of the called function." later in that same p15.Inhabit

© 2022 - 2024 — McMap. All rights reserved.