Page MenuHomeFreeBSD

dbm_nextkey: Always return an error if we've reached the end of the database
ClosedPublic

Authored by bnovkov on Jul 30 2025, 2:57 PM.
Tags
None
Referenced Files
Unknown Object (File)
Mon, Jun 15, 4:45 PM
Unknown Object (File)
Mon, Jun 15, 1:55 PM
Unknown Object (File)
Mon, Jun 15, 1:55 PM
Unknown Object (File)
Mon, Jun 15, 1:54 PM
Unknown Object (File)
Sun, Jun 14, 3:33 PM
Unknown Object (File)
May 13 2026, 5:03 AM
Unknown Object (File)
May 13 2026, 5:03 AM
Unknown Object (File)
May 13 2026, 5:03 AM

Details

Summary

POSIX.1 states that dbm_nextkey must return an invalid key
(i.e., key.dptr == NULL) after the end of the database was reached.
The current implementation of hash_seq will incorrectly restart
the key sequence after the end of the database is reached.

Fix this by checking the "current bucket" index when R_NEXT is passed.

Sponsored by: Klara, Inc.

Diff Detail

Repository
rG FreeBSD src repository
Lint
Lint Not Applicable
Unit
Tests Not Applicable

Event Timeline

markj added inline comments.
lib/libc/db/hash/hash.c
718

Is it legal to call this routine with flag == 0? The check above suggests so, but within libc there are no such calls. Are consumers allowed to call db->seq() directly?

Address @markj 's comments.

lib/libc/db/hash/hash.c
718

Good catch, thanks.
Consumers can call db->seq() directly, I've placed an additional check for flag == 0.

markj added inline comments.
lib/libc/db/hash/hash.c
707

The !flag test is redundant.

This revision is now accepted and ready to land.Jul 31 2025, 3:09 PM

I think this commit breaks the case, when cursor is not yet set:

R_NEXT  Retrieve the key/data pair immediately after the cursor.
        If the cursor is not yet set, this is the same as the
        R_FIRST flag.

at least we found that now it is required to pass R_FIRST in the read loop:

+       flags = R_FIRST;
-       while ((ret = dbh->seq(dbh, &keycol, &valuecol, R_NEXT)) == 0) {
+       while ((ret = dbh->seq(dbh, &keycol, &valuecol, flags)) == 0) {
+               if (flags == R_FIRST)
+                       flags = R_NEXT;

otherwise the application didn't see any entries in db file.

In D51635#1315903, @ae wrote:

I think this commit breaks the case, when cursor is not yet set:

R_NEXT  Retrieve the key/data pair immediately after the cursor.
        If the cursor is not yet set, this is the same as the
        R_FIRST flag.

at least we found that now it is required to pass R_FIRST in the read loop:

+       flags = R_FIRST;
-       while ((ret = dbh->seq(dbh, &keycol, &valuecol, R_NEXT)) == 0) {
+       while ((ret = dbh->seq(dbh, &keycol, &valuecol, flags)) == 0) {
+               if (flags == R_FIRST)
+                       flags = R_NEXT;

otherwise the application didn't see any entries in db file.

Thank you for letting me know, I've created a fix for this in D57670.