mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-09-20 22:52:00 +00:00
hashtable: fix environment variable corruption
Only first previously deleted entry was recognized, leading hsearch_r to think that there was no previously deleted entry. It then conluded that a free entry was found, even if there were no free entries and it overwrote a random entry. This patch makes sure all deleted or free entries are always found and also introduces constants for the 0 and -1 numbers. Unit tests to excersise a simple hash table usage and catch the corruption were added. To trash your environment, simply run this loop: setenv i 0 while true; do setenv v_$i $i setenv v_$i setexpr i $i + 1 done Signed-off-by: Roman Kapl <rka@sysgo.com>
This commit is contained in:
parent
4d9dbb1fbb
commit
9dfdbd9f0c
3 changed files with 136 additions and 3 deletions
|
@ -40,6 +40,9 @@
|
||||||
#define CONFIG_ENV_MAX_ENTRIES 512
|
#define CONFIG_ENV_MAX_ENTRIES 512
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define USED_FREE 0
|
||||||
|
#define USED_DELETED -1
|
||||||
|
|
||||||
#include <env_callback.h>
|
#include <env_callback.h>
|
||||||
#include <env_flags.h>
|
#include <env_flags.h>
|
||||||
#include <search.h>
|
#include <search.h>
|
||||||
|
@ -303,7 +306,7 @@ int hsearch_r(ENTRY item, ACTION action, ENTRY ** retval,
|
||||||
*/
|
*/
|
||||||
unsigned hval2;
|
unsigned hval2;
|
||||||
|
|
||||||
if (htab->table[idx].used == -1
|
if (htab->table[idx].used == USED_DELETED
|
||||||
&& !first_deleted)
|
&& !first_deleted)
|
||||||
first_deleted = idx;
|
first_deleted = idx;
|
||||||
|
|
||||||
|
@ -335,13 +338,17 @@ int hsearch_r(ENTRY item, ACTION action, ENTRY ** retval,
|
||||||
if (idx == hval)
|
if (idx == hval)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
if (htab->table[idx].used == USED_DELETED
|
||||||
|
&& !first_deleted)
|
||||||
|
first_deleted = idx;
|
||||||
|
|
||||||
/* If entry is found use it. */
|
/* If entry is found use it. */
|
||||||
ret = _compare_and_overwrite_entry(item, action, retval,
|
ret = _compare_and_overwrite_entry(item, action, retval,
|
||||||
htab, flag, hval, idx);
|
htab, flag, hval, idx);
|
||||||
if (ret != -1)
|
if (ret != -1)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
while (htab->table[idx].used);
|
while (htab->table[idx].used != USED_FREE);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* An empty bucket has been found. */
|
/* An empty bucket has been found. */
|
||||||
|
@ -433,7 +440,7 @@ static void _hdelete(const char *key, struct hsearch_data *htab, ENTRY *ep,
|
||||||
free(ep->data);
|
free(ep->data);
|
||||||
ep->callback = NULL;
|
ep->callback = NULL;
|
||||||
ep->flags = 0;
|
ep->flags = 0;
|
||||||
htab->table[idx].used = -1;
|
htab->table[idx].used = USED_DELETED;
|
||||||
|
|
||||||
--htab->filled;
|
--htab->filled;
|
||||||
}
|
}
|
||||||
|
|
1
test/env/Makefile
vendored
1
test/env/Makefile
vendored
|
@ -4,3 +4,4 @@
|
||||||
|
|
||||||
obj-y += cmd_ut_env.o
|
obj-y += cmd_ut_env.o
|
||||||
obj-y += attr.o
|
obj-y += attr.o
|
||||||
|
obj-y += hashtable.o
|
||||||
|
|
125
test/env/hashtable.c
vendored
Normal file
125
test/env/hashtable.c
vendored
Normal file
|
@ -0,0 +1,125 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
/*
|
||||||
|
* (C) Copyright 2019
|
||||||
|
* Roman Kapl, SYSGO, rka@sysgo.com
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <common.h>
|
||||||
|
#include <command.h>
|
||||||
|
#include <search.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <test/env.h>
|
||||||
|
#include <test/ut.h>
|
||||||
|
|
||||||
|
#define SIZE 32
|
||||||
|
#define ITERATIONS 10000
|
||||||
|
|
||||||
|
static int htab_fill(struct unit_test_state *uts,
|
||||||
|
struct hsearch_data *htab, size_t size)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
ENTRY item;
|
||||||
|
ENTRY *ritem;
|
||||||
|
char key[20];
|
||||||
|
|
||||||
|
for (i = 0; i < size; i++) {
|
||||||
|
sprintf(key, "%d", (int)i);
|
||||||
|
item.callback = NULL;
|
||||||
|
item.data = key;
|
||||||
|
item.flags = 0;
|
||||||
|
item.key = key;
|
||||||
|
ut_asserteq(1, hsearch_r(item, ENTER, &ritem, htab, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int htab_check_fill(struct unit_test_state *uts,
|
||||||
|
struct hsearch_data *htab, size_t size)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
ENTRY item;
|
||||||
|
ENTRY *ritem;
|
||||||
|
char key[20];
|
||||||
|
|
||||||
|
for (i = 0; i < size; i++) {
|
||||||
|
sprintf(key, "%d", (int)i);
|
||||||
|
item.callback = NULL;
|
||||||
|
item.flags = 0;
|
||||||
|
item.data = key;
|
||||||
|
item.key = key;
|
||||||
|
hsearch_r(item, FIND, &ritem, htab, 0);
|
||||||
|
ut_assert(ritem);
|
||||||
|
ut_asserteq_str(key, ritem->key);
|
||||||
|
ut_asserteq_str(key, ritem->data);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int htab_create_delete(struct unit_test_state *uts,
|
||||||
|
struct hsearch_data *htab, size_t iterations)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
ENTRY item;
|
||||||
|
ENTRY *ritem;
|
||||||
|
char key[20];
|
||||||
|
|
||||||
|
for (i = 0; i < iterations; i++) {
|
||||||
|
sprintf(key, "cd-%d", (int)i);
|
||||||
|
item.callback = NULL;
|
||||||
|
item.flags = 0;
|
||||||
|
item.data = key;
|
||||||
|
item.key = key;
|
||||||
|
hsearch_r(item, ENTER, &ritem, htab, 0);
|
||||||
|
ritem = NULL;
|
||||||
|
|
||||||
|
hsearch_r(item, FIND, &ritem, htab, 0);
|
||||||
|
ut_assert(ritem);
|
||||||
|
ut_asserteq_str(key, ritem->key);
|
||||||
|
ut_asserteq_str(key, ritem->data);
|
||||||
|
|
||||||
|
ut_asserteq(1, hdelete_r(key, htab, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Completely fill up the hash table */
|
||||||
|
static int env_test_htab_fill(struct unit_test_state *uts)
|
||||||
|
{
|
||||||
|
struct hsearch_data htab;
|
||||||
|
|
||||||
|
memset(&htab, 0, sizeof(htab));
|
||||||
|
ut_asserteq(1, hcreate_r(SIZE, &htab));
|
||||||
|
|
||||||
|
ut_assertok(htab_fill(uts, &htab, SIZE));
|
||||||
|
ut_assertok(htab_check_fill(uts, &htab, SIZE));
|
||||||
|
ut_asserteq(SIZE, htab.filled);
|
||||||
|
|
||||||
|
hdestroy_r(&htab);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ENV_TEST(env_test_htab_fill, 0);
|
||||||
|
|
||||||
|
/* Fill the hashtable up halfway an repeateadly delete/create elements
|
||||||
|
* and check for corruption
|
||||||
|
*/
|
||||||
|
static int env_test_htab_deletes(struct unit_test_state *uts)
|
||||||
|
{
|
||||||
|
struct hsearch_data htab;
|
||||||
|
|
||||||
|
memset(&htab, 0, sizeof(htab));
|
||||||
|
ut_asserteq(1, hcreate_r(SIZE, &htab));
|
||||||
|
|
||||||
|
ut_assertok(htab_fill(uts, &htab, SIZE / 2));
|
||||||
|
ut_assertok(htab_create_delete(uts, &htab, ITERATIONS));
|
||||||
|
ut_assertok(htab_check_fill(uts, &htab, SIZE / 2));
|
||||||
|
ut_asserteq(SIZE / 2, htab.filled);
|
||||||
|
|
||||||
|
hdestroy_r(&htab);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ENV_TEST(env_test_htab_deletes, 0);
|
Loading…
Reference in a new issue