Coverage Report

Created: 2026-05-16 07:38

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/suricata/src/util-rohash.c
Line
Count
Source
1
/* Copyright (C) 2007-2024 Open Information Security Foundation
2
 *
3
 * You can copy, redistribute or modify this Program under the terms of
4
 * the GNU General Public License version 2 as published by the Free
5
 * Software Foundation.
6
 *
7
 * This program is distributed in the hope that it will be useful,
8
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10
 * GNU General Public License for more details.
11
 *
12
 * You should have received a copy of the GNU General Public License
13
 * version 2 along with this program; if not, write to the Free Software
14
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15
 * 02110-1301, USA.
16
 */
17
18
/**
19
 * \file
20
 *
21
 * \author Victor Julien <victor@inliniac.net>
22
 *
23
 * Chained read only hash table implementation, meaning that
24
 * after the initial fill no changes are allowed.
25
 *
26
 * Loading takes 2 stages.
27
 * - stage1 maps data
28
 * - stage2 fills blob
29
 *
30
 * \todo a bloomfilter in the ROHashTableOffsets could possibly prevent
31
 *       a lot of cache misses when validating a potential match
32
 *
33
 * \todo maybe add a user ctx to be returned instead, something like a
34
 *       4/8 byte ptr or simply a flag
35
 */
36
37
#include "suricata-common.h"
38
#include "util-hash.h"
39
#include "util-unittest.h"
40
#include "util-memcmp.h"
41
#include "util-hash-lookup3.h"
42
#include "util-rohash.h"
43
#include "util-debug.h"
44
45
/** item_size data beyond this header */
46
typedef struct ROHashTableItem_ {
47
    uint32_t pos;       /**< position relative to other values with same hash */
48
    TAILQ_ENTRY(ROHashTableItem_) next;
49
} ROHashTableItem;
50
51
/** offset table */
52
typedef struct ROHashTableOffsets_ {
53
    uint32_t cnt;       /**< number of items for this hash */
54
    uint32_t offset;    /**< position in the blob of the first item */
55
} ROHashTableOffsets;
56
57
/** \brief initialize a new rohash
58
 *
59
 *  \param hash_bits hash size as 2^hash_bits, so power of 2, max 31
60
 *  \param item_size size of the data to store
61
 *
62
 *  \retval table ptr or NULL on error
63
 */
64
ROHashTable *ROHashInit(uint8_t hash_bits, uint16_t item_size)
65
1.30k
{
66
1.30k
    if (item_size % 4 != 0 || item_size == 0) {
67
0
        SCLogError("data size must be multiple of 4");
68
0
        return NULL;
69
0
    }
70
1.30k
    if (hash_bits < 4 || hash_bits > 31) {
71
0
        SCLogError("invalid hash_bits setting, valid range is 4-31");
72
0
        return NULL;
73
0
    }
74
75
1.30k
    uint32_t size = hashsize(hash_bits) * sizeof(ROHashTableOffsets);
76
77
1.30k
    ROHashTable *table = SCCalloc(1, sizeof(ROHashTable) + size);
78
1.30k
    if (unlikely(table == NULL)) {
79
0
        SCLogError("failed to alloc memory");
80
0
        return NULL;
81
0
    }
82
83
1.30k
    table->items = 0;
84
1.30k
    table->item_size = item_size;
85
1.30k
    table->hash_bits = hash_bits;
86
1.30k
    TAILQ_INIT(&table->head);
87
88
1.30k
    return table;
89
1.30k
}
90
91
void ROHashFree(ROHashTable *table)
92
1.30k
{
93
1.30k
    if (table != NULL) {
94
1.30k
        if (table->data != NULL) {
95
0
            SCFree(table->data);
96
0
        }
97
98
1.30k
        SCFree(table);
99
1.30k
    }
100
1.30k
}
101
102
uint32_t ROHashMemorySize(ROHashTable *table)
103
0
{
104
0
    uint32_t r1 = hashsize(table->hash_bits) * sizeof(ROHashTableOffsets);
105
0
    uint32_t r2 = table->items * table->item_size;
106
0
    return (uint32_t)(r1 + r2 + sizeof(ROHashTable));
107
0
}
108
109
/**
110
 *  \retval NULL not found
111
 *  \retval ptr found
112
 */
113
void *ROHashLookup(ROHashTable *table, void *data, uint16_t size)
114
0
{
115
0
    if (data == NULL || size != table->item_size) {
116
0
        SCReturnPtr(NULL, "void");
117
0
    }
118
119
0
    const uint32_t hash = hashword(data, table->item_size / 4, 0) & hashmask(table->hash_bits);
120
121
    /* get offsets start */
122
0
    const ROHashTableOffsets *os = (ROHashTableOffsets *)((uint8_t *)table + sizeof(ROHashTable));
123
0
    const ROHashTableOffsets *o = &os[hash];
124
125
    /* no matches */
126
0
    if (o->cnt == 0) {
127
0
        SCReturnPtr(NULL, "void");
128
0
    }
129
130
0
    for (uint32_t u = 0; u < o->cnt; u++) {
131
0
        uint32_t offset = (o->offset + u) * table->item_size;
132
133
0
        if (SCMemcmp(table->data + offset, data, table->item_size) == 0) {
134
0
            SCReturnPtr(table->data + offset, "void");
135
0
        }
136
0
    }
137
0
    SCReturnPtr(NULL, "void");
138
0
}
139
140
/** \brief Add a new value to the hash
141
 *
142
 *  \note can only be done when table isn't in a locked state yet
143
 *
144
 *  \param table the hash table
145
 *  \param value value to add
146
 *  \param size value size. *MUST* match table item_size
147
 *
148
 *  \retval 0 error
149
 *  \retval 1 ok
150
 */
151
int ROHashInitQueueValue(ROHashTable *table, void *value, uint16_t size)
152
0
{
153
0
    if (table->locked) {
154
0
        SCLogError("can't add value to locked table");
155
0
        return 0;
156
0
    }
157
0
    if (table->item_size != size) {
158
0
        SCLogError("wrong size for data %u != %u", size, table->item_size);
159
0
        return 0;
160
0
    }
161
162
0
    ROHashTableItem *item = SCCalloc(1, sizeof(ROHashTableItem) + table->item_size);
163
0
    if (item != NULL) {
164
0
        memcpy((void *)item + sizeof(ROHashTableItem), value, table->item_size);
165
0
        TAILQ_INSERT_TAIL(&table->head, item, next);
166
0
        return 1;
167
0
    }
168
169
0
    return 0;
170
0
}
171
172
/** \brief create final hash data structure
173
 *
174
 *  \param table the hash table
175
 *
176
 *  \retval 0 error
177
 *  \retval 1 ok
178
 *
179
 *  \note after this call the nothing can be added to the hash anymore.
180
 */
181
int ROHashInitFinalize(ROHashTable *table)
182
34
{
183
34
    if (table->locked) {
184
0
        SCLogError("table already locked");
185
0
        return 0;
186
0
    }
187
188
34
    ROHashTableItem *item = NULL;
189
34
    ROHashTableOffsets *os = (ROHashTableOffsets *)((uint8_t *)table + sizeof(ROHashTable));
190
191
    /* count items per hash value */
192
34
    TAILQ_FOREACH(item, &table->head, next) {
193
0
        uint32_t hash =
194
0
                hashword((uint32_t *)((uint8_t *)item + sizeof(*item)), table->item_size / 4, 0) &
195
0
                hashmask(table->hash_bits);
196
0
        ROHashTableOffsets *o = &os[hash];
197
198
0
        item->pos = o->cnt;
199
0
        o->cnt++;
200
0
        table->items++;
201
0
    }
202
203
34
    if (table->items == 0) {
204
34
        SCLogError("no items");
205
34
        return 0;
206
34
    }
207
208
    /* get the data block */
209
0
    uint32_t newsize = table->items * table->item_size;
210
0
    table->data = SCCalloc(1, newsize);
211
0
    if (table->data == NULL) {
212
0
        SCLogError("failed to alloc memory");
213
0
        return 0;
214
0
    }
215
216
    /* calc offsets into the block per hash value */
217
0
    uint32_t total = 0;
218
0
    for (uint32_t x = 0; x < hashsize(table->hash_bits); x++) {
219
0
        ROHashTableOffsets *o = &os[x];
220
221
0
        if (o->cnt == 0)
222
0
            continue;
223
224
0
        o->offset = total;
225
0
        total += o->cnt;
226
0
    }
227
228
    /* copy each value into the data block */
229
0
    TAILQ_FOREACH(item, &table->head, next) {
230
0
        uint32_t hash =
231
0
                hashword((uint32_t *)((uint8_t *)item + sizeof(*item)), table->item_size / 4, 0) &
232
0
                hashmask(table->hash_bits);
233
234
0
        ROHashTableOffsets *o = &os[hash];
235
0
        uint32_t offset = (o->offset + item->pos) * table->item_size;
236
237
0
        memcpy(table->data + offset, (uint8_t *)item + sizeof(*item), table->item_size);
238
0
    }
239
240
    /* clean up temp items */
241
0
    while ((item = TAILQ_FIRST(&table->head))) {
242
0
        TAILQ_REMOVE(&table->head, item, next);
243
0
        SCFree(item);
244
0
    }
245
246
0
    table->locked = 1;
247
0
    return 1;
248
0
}