Closed Bug 1403010 Opened 8 years ago Closed 8 years ago

Intermittent toolkit/components/aboutmemory/tests/test_memoryReporters.xul | application crashed [@ arena_salloc]

Categories

(Core :: Networking: Cache, defect, P5)

defect

Tracking

()

RESOLVED FIXED
mozilla58
Tracking Status
firefox-esr52 --- wontfix
firefox56 --- wontfix
firefox57 --- wontfix
firefox58 --- fixed

People

(Reporter: intermittent-bug-filer, Assigned: michal)

References

Details

(Keywords: crash, intermittent-failure)

Crash Data

Attachments

(1 file)

I looked at this a bit. The problem is this line in CacheFileMetadata::SizeOfExcludingThis(): > n += mallocSizeOf(mWriteBuf); That calls through to malloc_usable_size(), which calls isalloc_validate(), which determines that the pointer isn't null or near-null, and isn't at the start of a chunk. It then calls salloc(), which fails here: > MOZ_DIAGNOSTIC_ASSERT((mapbits & CHUNK_MAP_ALLOCATED) != 0); That suggests that mWriteBuf is a freed block. There are two places in netwerk/cache2/CacheFileMetadata.cpp that free mWriteBuf (using CacheFileUtils::FreeBuffer()) but they both immediately set mWriteBuf to nullptr afterwards, so I can't see how we could end up with this assertion failure... unless this code is running off the main thread and we got very unlucky and the mallocSizeof() call occurred after the `CacheFileUtils::FreeBuffer(mWriteBuf)` but before the `mWriteBuf = nullptr;`. Is that possible? Semi-relatedly, I did notice that mWriteBuf is not freed within ~CacheFileMetadata(). Should it be? Michal, are you able to answer the two questions above? Thank you.
Flags: needinfo?(michal.novotny)
BTW, I don't think this should necessarily block the stylo-intermittent-test-failures bug; it doesn't appear to have anything to do with Stylo.
(In reply to Nicholas Nethercote [:njn] from comment #1) > I looked at this a bit. The problem is this line in > CacheFileMetadata::SizeOfExcludingThis(): > > > n += mallocSizeOf(mWriteBuf); > > That calls through to malloc_usable_size(), which calls isalloc_validate(), > which determines that the pointer isn't null or near-null, and isn't at the > start of a chunk. It then calls salloc(), which fails here: > > > MOZ_DIAGNOSTIC_ASSERT((mapbits & CHUNK_MAP_ALLOCATED) != 0); > > That suggests that mWriteBuf is a freed block. There are two places in > netwerk/cache2/CacheFileMetadata.cpp that free mWriteBuf (using > CacheFileUtils::FreeBuffer()) but they both immediately set mWriteBuf to > nullptr afterwards, so I can't see how we could end up with this assertion > failure... unless this code is running off the main thread and we got very > unlucky and the mallocSizeof() call occurred after the > `CacheFileUtils::FreeBuffer(mWriteBuf)` but before the `mWriteBuf = > nullptr;`. Is that possible? Yes, this is usually called on the background thread. > Semi-relatedly, I did notice that mWriteBuf is not freed within > ~CacheFileMetadata(). Should it be? No. The buffer is allocated in CacheFileMetadata::WriteMetadata and released in CacheFileMetadata::OnDataWritten or in CacheFileMetadata::WriteMetadata when something fails and CacheFileMetadata::OnDataWritten wouldn't be called. I can add MOZ_ASSERT(!mWriteBuf) to ~CacheFileMetadata().
Assignee: nobody → michal.novotny
Component: about:memory → Networking: Cache
Flags: needinfo?(michal.novotny)
Product: Toolkit → Core
It looks like the same problem can happen with CacheFileMetadata::mBuf because it can be reallocated or freed on the background thread outside CacheFile's lock. Everything around CacheFile is protected by CacheFile::mLock, but the problem is that CacheFileMetadata doesn't have a reference to the CacheFile so it cannot grab the lock itself. Protected are only methods called from CacheFile which is not this case. I'd like to avoid adding lock just for memory reporting. What's a difference between mallocSizeOf(mWriteBuf) and the number passed to malloc()? We could use this number instead of calling mallocSizeOf().
Flags: needinfo?(n.nethercote)
> I'd like to avoid adding lock just for memory reporting. What's a difference between mallocSizeOf(mWriteBuf) > and the number passed to malloc()? We could use this number instead of calling mallocSizeOf(). The heap allocator rounds up many size requests, so that will underreport. Also, that doesn't give us DMD integration. There are some ways around this that I can use, but I'd like to clarify something... (In reply to Michal Novotny (:michal) from comment #4) > It looks like the same problem can happen with CacheFileMetadata::mBuf > because it can be reallocated or freed on the background thread outside > CacheFile's lock. Everything around CacheFile is protected by > CacheFile::mLock, but the problem is that CacheFileMetadata doesn't have a > reference to the CacheFile so it cannot grab the lock itself. Protected are > only methods called from CacheFile which is not this case. Can you describe exactly how the threads and locking work here? It sounds like this code is racy even if we ignore the memory reporting, though I may be misunderstanding. Thanks.
Flags: needinfo?(n.nethercote) → needinfo?(michal.novotny)
(In reply to Nicholas Nethercote [:njn] from comment #5) > Can you describe exactly how the threads and locking work here? It sounds > like this code is racy even if we ignore the memory reporting, though I may > be misunderstanding. Thanks. AFAICS, racy is only memory reporting. Normally, all methods are called from CacheFile and this is protected with CacheFile's lock. There are just 2 exceptions: 1) When reading metadata from the disk. CacheFileMetadata::ReadMetadata() is called (under the lock) and it calls CacheFileIOManager::Read(). When data are read from the disk, CacheFileIOManager calls CacheFileMetadata::OnDataRead() which tries to parse the metadata and then calls CacheFile::OnMetadataRead() which grabs the CacheFile's lock. Everything related to parsing metadata (which can cause reallocating buffer and reading more data from the disk, or freeing the buffer and failing with completely empty metadata) is executed outside the lock. But until the metadata is loaded and CacheFile::OnMetadataRead() is called, the CacheFile is not ready and is not used, so no method which could access the buffer is called from CacheFile. The only exception is memory reporting, which is called even if the metadata is being read. 2) Similar is writing of metadata. CacheFileMetadata::WriteMetadata() creates mWriteBuf (again under the CacheFile's lock) which is passed to CacheFileIOManager::Write(). It is freed in CacheFileMetadata::OnDataWritten(). The difference when comapred with (1) is that mWriteBuf is used only inside CacheFileMetadata so there is no problem to call CacheFileMetadata's methods when the metadata is being written. The exception is memory reporting again. The solution for (2) might be to not include mWriteBuf in memory reporting because this buffer exists for a short time only. To solve (1) we could exclude CacheFileMetadata which is being read from the disk from memory reporting. Nicholas, does this sound like a good solution?
Flags: needinfo?(michal.novotny) → needinfo?(n.nethercote)
> The solution for (2) might be to not include mWriteBuf in memory reporting > because this buffer exists for a short time only. To solve (1) we could > exclude CacheFileMetadata which is being read from the disk from memory > reporting. How would we exclude it in that case? > Nicholas, does this sound like a good solution? I think so. I didn't entirely follow your description. Excluding mWriteBuf is certainly safe :)
Flags: needinfo?(n.nethercote)
Attached patch fixSplinter Review
Attachment #8914728 - Flags: review?(valentin.gosu)
(In reply to Nicholas Nethercote [:njn] from comment #8) > > The solution for (2) might be to not include mWriteBuf in memory reporting > > because this buffer exists for a short time only. To solve (1) we could > > exclude CacheFileMetadata which is being read from the disk from memory > > reporting. > > How would we exclude it in that case? CacheFile::mReady is false until metadata is either read or created.
Attachment #8914728 - Flags: review?(valentin.gosu) → review+
Pushed by mnovotny@mozilla.com: https://hg.mozilla.org/integration/mozilla-inbound/rev/9c847604792e Crash due to race condition in CacheFileMetadata::SizeOfExcludingThis when reading or writing metadata, r=valentin
Status: NEW → RESOLVED
Closed: 8 years ago
Resolution: --- → FIXED
Target Milestone: --- → mozilla58
This code goes back as far as Gecko 30 AFAICT, but since there are no crash reports with this signature and OF only shows the one failure this bug was initially filed for, I'm marking older versions as wontfix. Feel free to set 57 back to affected and nominate for Beta uplift if you feel it's warranted, though.
Thank you for the fix, Michal. I think uplifting is reasonable, because it's a very simple fix.
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: