Closed
Bug 1354613
Opened 7 years ago
Closed 7 years ago
Registering multiple observers for a non-root pref branch is probably broken
Categories
(Core :: Preferences: Backend, enhancement)
Core
Preferences: Backend
Tracking
()
RESOLVED
FIXED
mozilla55
Tracking | Status | |
---|---|---|
firefox55 | --- | fixed |
People
(Reporter: erahm, Assigned: erahm)
Details
Attachments
(3 files, 1 obsolete file)
3.49 KB,
patch
|
benjamin
:
review+
|
Details | Diff | Splinter Review |
2.47 KB,
patch
|
benjamin
:
review+
|
Details | Diff | Splinter Review |
21.59 KB,
patch
|
froydnj
:
review+
|
Details | Diff | Splinter Review |
When you register a pref observer [1] for a non-root-pref-branch we calculate the full pref name [2]. Unfortunately when we do this we append the observer prefix to the pref branch name. So each time you add an observer the pref name grows, and is generally incorrect. Imagine you have prefBranch(foo), you register for "bar" now we have "foo.bar". Alright now imagine you register for "baz". Now you have "foo.barbaz". This is referred to as the "domain" in pref lingo. Alright now in pref_DoCallback we check if the changed pref prefix matches the domain [4], this will probably never match for any observers after the first one. I'll follow up with a gtest. [1] http://searchfox.org/mozilla-central/rev/fcd9f1480d65f1a5df2acda97eb07a7e133f6ed4/modules/libpref/nsPrefBranch.cpp#706 [2] http://searchfox.org/mozilla-central/rev/fcd9f1480d65f1a5df2acda97eb07a7e133f6ed4/modules/libpref/nsPrefBranch.cpp#741 [3] http://searchfox.org/mozilla-central/rev/fcd9f1480d65f1a5df2acda97eb07a7e133f6ed4/modules/libpref/nsPrefBranch.cpp#871-883 [4] http://searchfox.org/mozilla-central/rev/fcd9f1480d65f1a5df2acda97eb07a7e133f6ed4/modules/libpref/prefapi.cpp#993-1002
Assignee | ||
Comment 1•7 years ago
|
||
It also turns out test_libPrefs.js was implemented incorrectly and didn't actually test observing non-root pref branches.
Assignee | ||
Comment 2•7 years ago
|
||
Okay I missed where we truncate the branch name back to it's original size [1], so the values are fine (I've added a test). Where things get tricky are if we allow setting prefs off main thread (I think we do) then you can get into a racy state. Additionally in CollectReports we loop over prefs storing the name in a non-owning nsDependentCString [2] which could lead to invalid strings as well. [1] http://searchfox.org/mozilla-central/source/modules/libpref/nsPrefBranch.cpp#880 [2] http://searchfox.org/mozilla-central/rev/fcd9f1480d65f1a5df2acda97eb07a7e133f6ed4/modules/libpref/Preferences.cpp#355
Comment 3•7 years ago
|
||
The pref service is supposed to be main-thread only, both reading and writing.
Assignee | ||
Comment 4•7 years ago
|
||
(In reply to Eric Rahm [:erahm] from comment #2) > Okay I missed where we truncate the branch name back to it's original size > [1], so the values are fine (I've added a test). Where things get tricky are > if we allow setting prefs off main thread (I think we do) then you can get > into a racy state. And it turns out they're main thread only (except on B2G), the assertions are down in prefapi.cpp. It sounds like we might let stylo have off-main-thread access so perhaps we should still fix it.
Assignee | ||
Comment 5•7 years ago
|
||
This updates test_libPrefs.js so that it actually runs all the observer portion of it's tests. Previously the observer callback would get hit before the next do_test_pending call could be run, thus causing the test to end. An additional test was added to confirm that registering for multiple prefs on a non-root pref branch works as intended.
Attachment #8855910 -
Flags: review?(benjamin)
Assignee | ||
Comment 6•7 years ago
|
||
|getFloatPref| [1] is rather busted as well for non-root pref branches, we get the full pref name, then pass that to another function that gets the full pref name. So we run into a possible UAF if the string mutates before appending or just a weird name. The function isn't widely used, but should be fixed. [1] http://searchfox.org/mozilla-central/rev/fcd9f1480d65f1a5df2acda97eb07a7e133f6ed4/modules/libpref/nsPrefBranch.cpp#194-196
Assignee | ||
Comment 7•7 years ago
|
||
MozReview-Commit-ID: 1NLBQQ40sae
Attachment #8855947 -
Flags: review?(benjamin)
Assignee | ||
Comment 8•7 years ago
|
||
This adds a PrefName wrapper that allows use to avoid copying the pref name string in the fast root pref branch case, but also allows to create a new string in the slower non-root pref branch case. By creating a new string we avoid the odd behavior of mutating the pref branch name with each retrieval. Nathan, this does some trickery with Variants and move operators that I'm not super familiar with so feedback there would be helpful. Also some confirmation that this should be reasonably fast would be great.
Attachment #8855972 -
Flags: review?(nfroyd)
Comment 9•7 years ago
|
||
Comment on attachment 8855972 [details] [diff] [review] Stop mutating the pref branch name when retrieving it Review of attachment 8855972 [details] [diff] [review]: ----------------------------------------------------------------- This seems like a decent cleanup. r=me with some discussion below. ::: modules/libpref/nsPrefBranch.cpp @@ +875,2 @@ > > + return PrefName(mPrefRoot + nsDependentCString(aPrefName)); This will allocate a new string every time, in contrast to the old code, which would allocate up to the longest string requested. So that's a minus. But given that nsPrefBranch is basically a JS-only API, I'm inclined to guess that the small inefficiency here doesn't matter very much in the grand scheme of things. ::: modules/libpref/nsPrefBranch.h @@ +192,2 @@ > > + int32_t GetRootLength() { return mPrefRoot.Length(); } This can be a `const` method. @@ +210,5 @@ > + typedef mozilla::Variant<const char*, const nsCString> StorageType; > + > + explicit PrefName(const char* aName) : mValue(aName) {} > + explicit PrefName(const nsCString& aName) : mValue(aName) {} > + PrefName(PrefName&& aOther) : mValue(mozilla::Forward<StorageType>(aOther.mValue)) {} Are you defining the move constructor because the copy constructor is deleted, and the compiler complains about returning PrefName values otherwise? Do you think it would hurt to just let these be defaulted, or are you trying to make this maximally efficient if nsCString ever grows a move constructor? @@ +211,5 @@ > + > + explicit PrefName(const char* aName) : mValue(aName) {} > + explicit PrefName(const nsCString& aName) : mValue(aName) {} > + PrefName(PrefName&& aOther) : mValue(mozilla::Forward<StorageType>(aOther.mValue)) {} > + PrefName(const PrefName&) = delete; If you're going to delete the copy constructor, you should delete operator=(const PrefName&) as well. @@ +245,5 @@ > nsresult CheckSanityOfStringLength(const char* aPrefName, const nsACString& aValue); > nsresult CheckSanityOfStringLength(const char* aPrefName, const char* aValue); > nsresult CheckSanityOfStringLength(const char* aPrefName, const uint32_t aLength); > void RemoveExpiredCallback(PrefCallback *aCallback); > + PrefName getPrefName(const char *aPrefName); This can be a `const` method now too.
Attachment #8855972 -
Flags: review?(nfroyd) → review+
Assignee | ||
Comment 10•7 years ago
|
||
Updated per review, should probably take a second look at the header. I switched to deriving from Variant and using the |match| function.
Attachment #8856775 -
Flags: review?(nfroyd)
Assignee | ||
Updated•7 years ago
|
Attachment #8855972 -
Attachment is obsolete: true
Comment 11•7 years ago
|
||
Comment on attachment 8856775 [details] [diff] [review] Part 3: Stop mutating the pref branch name when retrieving it Review of attachment 8856775 [details] [diff] [review]: ----------------------------------------------------------------- Thanks.
Attachment #8856775 -
Flags: review?(nfroyd) → review+
Updated•7 years ago
|
Attachment #8855910 -
Flags: review?(benjamin) → review+
Comment 12•7 years ago
|
||
Comment on attachment 8855947 [details] [diff] [review] Part 2: Fix GetFloatPref on non-root branches wow
Attachment #8855947 -
Flags: review?(benjamin) → review+
Assignee | ||
Comment 13•7 years ago
|
||
https://hg.mozilla.org/integration/mozilla-inbound/rev/f256046af6059da5ae9a8e3e23426944f40975e1 Bug 1354613 - Part 1: Fix the observer portion of test_libPrefs.js. r=bsmedberg https://hg.mozilla.org/integration/mozilla-inbound/rev/9c6ed9355ff926f4f56506186bf4690f7bfb252b Bug 1354613 - Part 2: Fix GetFloatPref on non-root branches. r=bsmedberg https://hg.mozilla.org/integration/mozilla-inbound/rev/89aacb54a8826d09a2b362e2aa61dde08631f175 Bug 1354613 - Part 3: Stop mutating the pref branch name when retrieving it. r=froydnj
Comment 14•7 years ago
|
||
bugherder |
https://hg.mozilla.org/mozilla-central/rev/f256046af605 https://hg.mozilla.org/mozilla-central/rev/9c6ed9355ff9 https://hg.mozilla.org/mozilla-central/rev/89aacb54a882
Status: NEW → RESOLVED
Closed: 7 years ago
Resolution: --- → FIXED
Target Milestone: --- → mozilla55
You need to log in
before you can comment on or make changes to this bug.
Description
•