Can a find() on an empty unordered_map lead to an access violation?
Asked Answered
A

2

6

I'm investigating a dump, caused by an access violation.
On that particular line of code, there is the following line:

if (internal_map.find(uiElemKey) == internal_map.end() || 
    internal_map[uiElemKey].find(m_iPID) == internal_map[uiElemKey].end() || 
    internal_map[uiElemKey][m_iPID].find(idx) == internal_map[uiElemKey][m_iPID].end()) {

In the watch-window I can see that the amount of entries in internal_map equals 0.

In my opinion, the access violation might be caused by following reasons:

  1. As there are no entries in the map, the find() method generates an exception.
  2. As the are no entries in the map, the end() method generates an exception.
  3. The find() method and the end() method are working fine, but give different results, which leads to the following condition, where an access violation happens.

I think that the access violation can be avoided, using this condition:

if (internal_map.size() == 0                                              ||
    internal_map.find(uiElemKey) == internal_map.end()                    || 
    internal_map[uiElemKey].find(m_iPID) == internal_map[uiElemKey].end() || 
    internal_map[uiElemKey][m_iPID].find(idx) == internal_map[uiElemKey][m_iPID].end()) {

Can somebody confirm this is correct and explain which reason is the correct one?
Thanks in advance

P.s. for your information: I'm doing dump analysis, and it's almost impossible to reproduce the mentioned problem, so just trying and see what happens is not an option.

Automatic answered 25/7, 2018 at 8:35 Comment(8)
No exceptions when calling find on empty map. I suspect your map is corrupted before the call.Sandie
Btw, by using iterator returned by find, you might avoid repetitive look-ups.Sandie
Look at other internal members might give indication.Sandie
If the map is empty can you produce a complete 5 line example program that reproduced the error?Amphitrite
@Galik: it's not that simple: it seems that my map has been modified by other threads, which makes it very difficult to reproduce.Automatic
If you have multiple thread, you should protect your data (mutex) to avoid UB by possibly writing and reading the same variable at the same time.Sandie
I had an issue that gave the same impression about the empty map causing the problem. But in my case, it turned out that something else was wrong with my if-statement that surfaced only when the map was empty.Lyn
Does the dump have enough information that you can deduce which part of that expression is triggering the access violation? Can you tell where the address of the access violation is (e.g., in the internal_map, the uiElemKey, the code)? Does the code overload the hash function for your key type and could the exception be happening there? Does the access violation happen on a read or a write operation?Confine
R
5

Assuming that the unordered map is not being modified by some other thread when find() is executing, there is no chance that find() or end() will throw an exception or crash. Their behavior is defined for empty container. So there is no way to corrupt the map by just calling them. Saying this, your test for size() is redundant.

Robena answered 25/7, 2018 at 8:42 Comment(6)
That was also my idea: launching find() or end() does not cause an access violation. But if both methods give different results, then the map gets accessed (internal_map[uiElemKey]) and I'm afraid that this might cause an access violation. Do you think that is possible?Automatic
@Automatic Before accessing internal_map[uiElemKey] you have already checked that uiElemKey exists. I can't see any reason for them to generate access violation.Robena
@Automatic the order of evaluation for && || operators are also defined.Robena
That's exactly my problem: I've executed a find(uiElemKey) and I have checked if this equals the end() method. As mentioned in some examples on the web, this way of working is working correctly (but all the examples are treating maps which are not empty). I just want to be sure that, in case of an empty map, the result of find(anything) and end() are also equal.Automatic
@Automatic yes they are also equal for empty containers. Whenever a key is not present it will return end(). Whether the container is empty or not doesn't matter.Robena
@Dominique: The single solution to make an object safe to access from multiple threads is std::mutex. That's not just for threads altering an object, even the reader threads need it.Archine
V
3

As there are no entries in the map, the find() method generates an exception

No, if your program is valid (there is no UB somewhere earlier) find() method will return past-the-end iterator which equals to iterator returned by end() in the same line of code.

Varistor answered 25/7, 2018 at 8:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.