Skip to content

memenv: register the FileState created by NewAppendableFile - #1351

Open
xhon-pelushi wants to merge 1 commit into
google:mainfrom
xhon-pelushi:fix/1328-memenv-appendable
Open

xhon-pelushi wants to merge 1 commit into
google:mainfrom
xhon-pelushi:fix/1328-memenv-appendable

Conversation

@xhon-pelushi

Copy link
Copy Markdown

Fixes #1328.

InMemoryEnv::NewAppendableFile() takes a reference to the map slot and, when the file does not exist yet, creates a FileState and Ref()s it — but never stores it back:

FileState** sptr = &file_map_[fname];   // operator[] inserts, value-initialised to nullptr
FileState* file = *sptr;
if (file == nullptr) {
  file = new FileState();
  file->Ref();
  // *sptr is never assigned
}

operator[] has already inserted the key, so the name stays mapped to nullptr while FileExists() reports it as present.

It crashes sooner than the issue suggests

The report describes later lookups dereferencing the null. No second lookup is needed, because ~InMemoryEnv() unrefs every value in the map (memenv.cc:227):

Env* env = NewMemEnv(Env::Default());
WritableFile* file;
env->NewAppendableFile("/dir/f", &file);
delete file;
delete env;      // SEGV

Under ASan:

ERROR: AddressSanitizer: SEGV on unknown address 0x000000000010
The signal is caused by a READ memory access.
    #0 ___pthread_mutex_lock
    #5 Unref  helpers/memenv/memenv.cc:44
    #6 ~InMemoryEnv  helpers/memenv/memenv.cc:227

0x10 is the offset of the mutex inside FileState, read through a null this.

The Ref() also leaked: WritableFileImpl takes and drops its own reference in its constructor/destructor, so nothing owned the one created here.

Fix

One line — *sptr = file; — which makes the appendable path match NewWritableFile() a few lines above, which already does file_map_[fname] = file; for exactly this case.

Why this was not already caught

MemEnvTest.Basics calls NewAppendableFile("/dir/f", …) only after NewWritableFile() has created that same file, so it always takes the file != nullptr branch. The new-file branch had no coverage.

The added test appends to a fresh name, checks the size, reopens for append to confirm it extends rather than replaces, and reads the contents back.

Testing

Run Before After
MemEnvTest.AppendableFileRegistersNewFile SIGSEGV (139) pass
MemEnvTest.* — 8/8
ctest (Debug) 3/3 3/3
leveldb_tests under ASan SEGV 210 passed, 2 skipped, 0 failed

The 2 skips are the compression tests, which need snappy/zstd.

NewAppendableFile() takes a reference to the map slot, and when the file does
not exist yet it creates a FileState and Refs it -- but never stores it back
into the slot. The slot was default-constructed to nullptr by operator[], so
the name stays mapped to a null FileState while FileExists() reports it as
present.

Every later use of that name dereferences the null pointer. The shortest path
to a crash does not even need a second lookup, because ~InMemoryEnv() Unrefs
every value in the map:

    Env* env = NewMemEnv(Env::Default());
    WritableFile* file;
    env->NewAppendableFile("/dir/f", &file);
    delete file;
    delete env;    // SEGV in FileState::Unref()

Under ASan that reports as "SEGV on unknown address 0x000000000010 ... READ
memory access" in ___pthread_mutex_lock, reached from FileState::Unref() at
memenv.cc:44 via ~InMemoryEnv() at memenv.cc:227 - 0x10 being the offset of
the mutex inside FileState, read through a null this.

The Ref() also leaked, since nothing owned that reference: WritableFileImpl
takes its own in the constructor and drops it in the destructor.

Store the new FileState in the slot, matching NewWritableFile(), which already
does file_map_[fname] = file for the same case. The existing coverage missed
this because MemEnvTest.Basics only calls NewAppendableFile() on a file that
NewWritableFile() has already created, which takes the other branch.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: NewAppendableFile does not record newly created FileState in file_map_

1 participant