Skip to content

Commit 196c8ec

Browse files
committed
[Runtime] Fix PrebuiltStringMap find with non-terminated keys.
Don't use strcmp to compare the candidate key with the search key, as the search key may not be NUL terminated. Use strncmp and a length check on the candidate key.
1 parent 46ddceb commit 196c8ec

File tree

2 files changed

+57
-37
lines changed

2 files changed

+57
-37
lines changed

include/swift/Runtime/PrebuiltStringMap.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,10 @@ struct PrebuiltStringMap {
160160

161161
size_t numSearched = 0;
162162
while (const char *key = array()[index].key) {
163-
if (strcmp(key, toFind) == 0)
163+
// key is NUL terminated but toFind may not be. Check that they have equal
164+
// contents up to len, and check that key has a terminating NUL at the
165+
// right point.
166+
if (strncmp(key, toFind, len) == 0 && key[len] == 0)
164167
return &array()[index];
165168

166169
index = index + 1;

unittests/runtime/PrebuiltStringMap.cpp

Lines changed: 53 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -16,40 +16,57 @@
1616
static bool stringIsNull(const char *str) { return str == nullptr; }
1717

1818
TEST(PrebuiltStringMapTest, basic) {
19-
unsigned testEntryCount = 1000;
20-
21-
std::vector<std::pair<std::string, unsigned>> testVector;
22-
testVector.reserve(testEntryCount);
23-
for (unsigned i = 0; i < testEntryCount; i++) {
24-
std::string key;
25-
for (unsigned j = 0; j < i; j++)
26-
key += std::to_string(j);
27-
testVector.push_back({key, i});
28-
}
29-
30-
using Map = swift::PrebuiltStringMap<const char *, unsigned, stringIsNull>;
31-
32-
unsigned mapSize = testEntryCount * 4 / 3;
33-
void *mapAllocation = calloc(1, Map::byteSize(mapSize));
34-
Map *map = new (mapAllocation) Map(mapSize);
35-
36-
for (auto &[key, value] : testVector) {
37-
const char *keyCStr = key.c_str();
38-
auto *element = map->insert(keyCStr);
39-
EXPECT_NE(element, nullptr);
40-
41-
element->key = keyCStr;
42-
element->value = value;
43-
}
44-
45-
for (auto &[key, value] : testVector) {
46-
const char *keyCStr = key.c_str();
47-
auto *element = map->find(keyCStr);
48-
EXPECT_NE(element, nullptr);
49-
50-
EXPECT_EQ(element->key, key);
51-
EXPECT_EQ(element->value, value);
52-
}
53-
54-
free(mapAllocation);
19+
auto testOnce = [&](unsigned testEntryCount) {
20+
std::vector<std::pair<std::string, unsigned>> testVector;
21+
testVector.reserve(testEntryCount);
22+
for (unsigned i = 0; i < testEntryCount; i++) {
23+
std::string key;
24+
for (unsigned j = 0; j < i; j++)
25+
key += std::to_string(j);
26+
testVector.push_back({key, i});
27+
}
28+
29+
using Map = swift::PrebuiltStringMap<const char *, unsigned, stringIsNull>;
30+
31+
unsigned mapSize = testEntryCount * 4 / 3;
32+
void *mapAllocation = calloc(1, Map::byteSize(mapSize));
33+
Map *map = new (mapAllocation) Map(mapSize);
34+
35+
for (auto &[key, value] : testVector) {
36+
const char *keyCStr = key.c_str();
37+
auto *element = map->insert(keyCStr);
38+
EXPECT_NE(element, nullptr);
39+
40+
element->key = keyCStr;
41+
element->value = value;
42+
}
43+
44+
for (auto &[key, value] : testVector) {
45+
const char *keyCStr = key.c_str();
46+
auto *element = map->find(keyCStr);
47+
EXPECT_NE(element, nullptr);
48+
49+
EXPECT_EQ(element->key, key);
50+
EXPECT_EQ(element->value, value);
51+
}
52+
53+
// Test the interface that takes a string and length without NUL
54+
// termination.
55+
for (auto &[key, value] : testVector) {
56+
auto keyCopy = key;
57+
keyCopy += "xyz";
58+
const char *keyCStr = keyCopy.c_str();
59+
auto *element = map->find(keyCStr, key.size());
60+
EXPECT_NE(element, nullptr);
61+
62+
EXPECT_EQ(element->key, key);
63+
EXPECT_EQ(element->value, value);
64+
}
65+
66+
free(mapAllocation);
67+
};
68+
69+
testOnce(10);
70+
testOnce(100);
71+
testOnce(1000);
5572
}

0 commit comments

Comments
 (0)