Bug 212695

Summary: [EME] Create CDMProxyFactory
Product: WebKit Reporter: Xabier Rodríguez Calvar <calvaris>
Component: New BugsAssignee: Xabier Rodríguez Calvar <calvaris>
Status: RESOLVED FIXED    
Severity: Normal CC: annulen, benjamin, cdumez, cgarcia, cmarcelo, cturner, eric.carlson, ews-watchlist, glenn, gustavo, gyuyoung.kim, jer.noble, menard, philipj, pnormand, ryuan.choi, sergio, vjaquez, webkit-bug-importer, youennf
Priority: P2 Keywords: InRadar
Version: WebKit Nightly Build   
Hardware: Unspecified   
OS: Unspecified   
Attachments:
Description Flags
Patch
none
Patch
none
Patch
none
Patch
none
Patch
none
Patch
none
Patch
none
Patch
none
Patch
none
Patch
none
Patch
none
Patch
none
Patch
youennf: review+
Patch none

Xabier Rodríguez Calvar
Reported 2020-06-03 07:46:19 PDT
[EME] Create CDMProxyFactory
Attachments
Patch (12.41 KB, patch)
2020-06-03 08:22 PDT, Xabier Rodríguez Calvar
no flags
Patch (12.41 KB, patch)
2020-06-03 09:15 PDT, Xabier Rodríguez Calvar
no flags
Patch (12.42 KB, patch)
2020-06-04 00:04 PDT, Xabier Rodríguez Calvar
no flags
Patch (12.35 KB, patch)
2020-06-04 00:12 PDT, Xabier Rodríguez Calvar
no flags
Patch (12.39 KB, patch)
2020-06-04 00:44 PDT, Xabier Rodríguez Calvar
no flags
Patch (11.68 KB, patch)
2020-06-04 00:53 PDT, Xabier Rodríguez Calvar
no flags
Patch (18.70 KB, patch)
2020-06-10 02:43 PDT, Xabier Rodríguez Calvar
no flags
Patch (27.15 KB, patch)
2020-06-10 03:52 PDT, Xabier Rodríguez Calvar
no flags
Patch (27.17 KB, patch)
2020-06-10 04:25 PDT, Xabier Rodríguez Calvar
no flags
Patch (27.57 KB, patch)
2020-06-11 06:29 PDT, Xabier Rodríguez Calvar
no flags
Patch (30.14 KB, patch)
2020-06-11 07:04 PDT, Xabier Rodríguez Calvar
no flags
Patch (30.17 KB, patch)
2020-06-12 01:19 PDT, Xabier Rodríguez Calvar
no flags
Patch (16.00 KB, patch)
2020-06-15 08:07 PDT, Xabier Rodríguez Calvar
youennf: review+
Patch (16.07 KB, patch)
2020-06-16 07:34 PDT, Xabier Rodríguez Calvar
no flags
Xabier Rodríguez Calvar
Comment 1 2020-06-03 08:22:02 PDT
Xabier Rodríguez Calvar
Comment 2 2020-06-03 09:15:33 PDT
Xabier Rodríguez Calvar
Comment 3 2020-06-04 00:04:11 PDT
Xabier Rodríguez Calvar
Comment 4 2020-06-04 00:12:34 PDT
Xabier Rodríguez Calvar
Comment 5 2020-06-04 00:44:21 PDT
Xabier Rodríguez Calvar
Comment 6 2020-06-04 00:53:51 PDT
Charlie Turner
Comment 7 2020-06-05 03:48:02 PDT
I see this (some of the crashes are racey after apply this). The setProxy call has important timing preconditions which the ctor does not meet. Regressions: Unexpected crashes (5) imported/w3c/web-platform-tests/encrypted-media/clearkey-mp4-playback-temporary-waitingforkey.https.html [ Crash ] imported/w3c/web-platform-tests/encrypted-media/clearkey-mp4-setmediakeys-again-after-resetting-src.https.html [ Crash ] imported/w3c/web-platform-tests/encrypted-media/clearkey-mp4-syntax-mediakeys.https.html [ Crash ] imported/w3c/web-platform-tests/encrypted-media/clearkey-mp4-syntax-mediakeysession.https.html [ Crash ] media/encrypted-media/clearKey/clearKey-encrypted-cenc-event.html [ Crash ]
Xabier Rodríguez Calvar
Comment 8 2020-06-10 02:43:03 PDT
Created attachment 401527 [details] Patch (In reply to Charlie Turner from comment #7) > I see this (some of the crashes are racey after apply this). The setProxy > call has important timing preconditions which the ctor does not meet. > > Regressions: Unexpected crashes (5) > > imported/w3c/web-platform-tests/encrypted-media/clearkey-mp4-playback- > temporary-waitingforkey.https.html [ Crash ] > > imported/w3c/web-platform-tests/encrypted-media/clearkey-mp4-setmediakeys- > again-after-resetting-src.https.html [ Crash ] > > imported/w3c/web-platform-tests/encrypted-media/clearkey-mp4-syntax- > mediakeys.https.html [ Crash ] > > imported/w3c/web-platform-tests/encrypted-media/clearkey-mp4-syntax- > mediakeysession.https.html [ Crash ] > media/encrypted-media/clearKey/clearKey-encrypted-cenc-event.html [ Crash ] With the obsoleted patch these tests are flaky but not because of the key stuff, only because we initialize GCrypt too soon. For that I created LazilyInitialized and get that initialize when needed (I had the manual lazy initalization version but writing a class for this seems possibly useful for more people). I'm writing tests for it but I wanted to post it here to get earlier reviews about it.
Xabier Rodríguez Calvar
Comment 9 2020-06-10 03:52:29 PDT
Created attachment 401530 [details] Patch Added a test for the LazilyInitialized class.
Xabier Rodríguez Calvar
Comment 10 2020-06-10 04:25:21 PDT
Created attachment 401532 [details] Patch This the style issues. The remaining one is a false positive.
youenn fablet
Comment 11 2020-06-10 06:25:43 PDT
Comment on attachment 401532 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=401532&action=review > Source/WebCore/platform/graphics/gstreamer/eme/CDMProxyClearKey.cpp:91 > + gcry_cipher_close(*m_gCryptHandle); How about having a unique_ptr or an Optional of a class which would wrap a gcry_cipher_hd_t. Class constructor would call gcry_cipher_open and destructor would call gcry_cipher_close. You could lazily initialise it in the gCryptHandle() getter that you would use everywhere instead of m_gCryptHandle.
Xabier Rodríguez Calvar
Comment 12 2020-06-10 07:32:24 PDT
(In reply to youenn fablet from comment #11) > Comment on attachment 401532 [details] > Patch > > View in context: > https://bugs.webkit.org/attachment.cgi?id=401532&action=review > > > Source/WebCore/platform/graphics/gstreamer/eme/CDMProxyClearKey.cpp:91 > > + gcry_cipher_close(*m_gCryptHandle); > > How about having a unique_ptr or an Optional of a class which would wrap a > gcry_cipher_hd_t. Class constructor would call gcry_cipher_open and > destructor would call gcry_cipher_close. > You could lazily initialise it in the gCryptHandle() getter that you would > use everywhere instead of m_gCryptHandle. Class constructor can't call gcry_cipher_open because that creates test flakiness (getting too much unnecessary secure memory) and that's why we have to initialize it lazily. Of course I can initialize it manually with an Optional or std::unique_ptr but I thought that creating a class at WTF for that could be useful for more developers.
Xabier Rodríguez Calvar
Comment 13 2020-06-10 07:35:50 PDT
(In reply to Xabier Rodríguez Calvar from comment #12) > > > Source/WebCore/platform/graphics/gstreamer/eme/CDMProxyClearKey.cpp:91 > > > + gcry_cipher_close(*m_gCryptHandle); > > > > How about having a unique_ptr or an Optional of a class which would wrap a > > gcry_cipher_hd_t. Class constructor would call gcry_cipher_open and > > destructor would call gcry_cipher_close. > > You could lazily initialise it in the gCryptHandle() getter that you would > > use everywhere instead of m_gCryptHandle. > > Class constructor can't call gcry_cipher_open because that creates test > flakiness (getting too much unnecessary secure memory) and that's why we > have to initialize it lazily. Of course I can initialize it manually with an > Optional or std::unique_ptr but I thought that creating a class at WTF for > that could be useful for more developers. About checking in the destructor for the handle to be initialized, it is because if is not initialized it does not make sense to close it.
youenn fablet
Comment 14 2020-06-10 08:19:52 PDT
(In reply to Xabier Rodríguez Calvar from comment #12) > (In reply to youenn fablet from comment #11) > > Comment on attachment 401532 [details] > > Patch > > > > View in context: > > https://bugs.webkit.org/attachment.cgi?id=401532&action=review > > > > > Source/WebCore/platform/graphics/gstreamer/eme/CDMProxyClearKey.cpp:91 > > > + gcry_cipher_close(*m_gCryptHandle); > > > > How about having a unique_ptr or an Optional of a class which would wrap a > > gcry_cipher_hd_t. Class constructor would call gcry_cipher_open and > > destructor would call gcry_cipher_close. > > You could lazily initialise it in the gCryptHandle() getter that you would > > use everywhere instead of m_gCryptHandle. > > Class constructor can't call gcry_cipher_open because that creates test > flakiness (getting too much unnecessary secure memory) and that's why we > have to initialize it lazily. Of course I can initialize it manually with an > Optional or std::unique_ptr but I thought that creating a class at WTF for > that could be useful for more developers. I was meaning something like: class CDMProxyClearKey { ... Optional<GCryptHandle> m_cipherHandle; }; gcry_cipher_hd_t& CDMProxyClearKey::cipherHandle() { // Lazily created. if (! m_cipherHandle) m_handle = GCryptHandle(); return m_cipherHandle->handle(); } class GCryptHandle { GCryptHandle() // constructor will call gcry_cipher_open ~GCryptHandle() // destructor will call gcry_cipher_close ... gcry_cipher_hd_t m_handle; };
Xabier Rodríguez Calvar
Comment 15 2020-06-10 09:07:15 PDT
(In reply to youenn fablet from comment #14) > (In reply to Xabier Rodríguez Calvar from comment #12) > > (In reply to youenn fablet from comment #11) > > > Comment on attachment 401532 [details] > > > Patch > > > > > > View in context: > > > https://bugs.webkit.org/attachment.cgi?id=401532&action=review > > > > > > > Source/WebCore/platform/graphics/gstreamer/eme/CDMProxyClearKey.cpp:91 > > > > + gcry_cipher_close(*m_gCryptHandle); > > > > > > How about having a unique_ptr or an Optional of a class which would wrap a > > > gcry_cipher_hd_t. Class constructor would call gcry_cipher_open and > > > destructor would call gcry_cipher_close. > > > You could lazily initialise it in the gCryptHandle() getter that you would > > > use everywhere instead of m_gCryptHandle. > > > > Class constructor can't call gcry_cipher_open because that creates test > > flakiness (getting too much unnecessary secure memory) and that's why we > > have to initialize it lazily. Of course I can initialize it manually with an > > Optional or std::unique_ptr but I thought that creating a class at WTF for > > that could be useful for more developers. > > I was meaning something like: > class CDMProxyClearKey { > ... > Optional<GCryptHandle> m_cipherHandle; > }; > > gcry_cipher_hd_t& CDMProxyClearKey::cipherHandle() > { > // Lazily created. > if (! m_cipherHandle) > m_handle = GCryptHandle(); > return m_cipherHandle->handle(); > } > > class GCryptHandle { > GCryptHandle() // constructor will call gcry_cipher_open > ~GCryptHandle() // destructor will call gcry_cipher_close > ... > gcry_cipher_hd_t m_handle; > }; To make this, I prefer a more general class that brings already the lazy implementation. Creating a class brings nothing if you just use an Optional or a unique_ptr.
Xabier Rodríguez Calvar
Comment 16 2020-06-10 09:09:11 PDT
(In reply to Xabier Rodríguez Calvar from comment #15) > (In reply to youenn fablet from comment #14) > > (In reply to Xabier Rodríguez Calvar from comment #12) > > > (In reply to youenn fablet from comment #11) > > > > Comment on attachment 401532 [details] > > > > Patch > > > > > > > > View in context: > > > > https://bugs.webkit.org/attachment.cgi?id=401532&action=review > > > > > > > > > Source/WebCore/platform/graphics/gstreamer/eme/CDMProxyClearKey.cpp:91 > > > > > + gcry_cipher_close(*m_gCryptHandle); > > > > > > > > How about having a unique_ptr or an Optional of a class which would wrap a > > > > gcry_cipher_hd_t. Class constructor would call gcry_cipher_open and > > > > destructor would call gcry_cipher_close. > > > > You could lazily initialise it in the gCryptHandle() getter that you would > > > > use everywhere instead of m_gCryptHandle. > > > > > > Class constructor can't call gcry_cipher_open because that creates test > > > flakiness (getting too much unnecessary secure memory) and that's why we > > > have to initialize it lazily. Of course I can initialize it manually with an > > > Optional or std::unique_ptr but I thought that creating a class at WTF for > > > that could be useful for more developers. > > > > I was meaning something like: > > class CDMProxyClearKey { > > ... > > Optional<GCryptHandle> m_cipherHandle; > > }; > > > > gcry_cipher_hd_t& CDMProxyClearKey::cipherHandle() > > { > > // Lazily created. > > if (! m_cipherHandle) > > m_handle = GCryptHandle(); > > return m_cipherHandle->handle(); > > } > > > > class GCryptHandle { > > GCryptHandle() // constructor will call gcry_cipher_open > > ~GCryptHandle() // destructor will call gcry_cipher_close > > ... > > gcry_cipher_hd_t m_handle; > > }; > > To make this, I prefer a more general class that brings already the lazy > implementation. Creating a class brings nothing if you just use an Optional > or a unique_ptr. Maybe a class that inherits from LazilyInitialized that forces you to have an virtual initialize = 0 method that is automatically called when you access the operator* or operator->
Xabier Rodríguez Calvar
Comment 17 2020-06-11 06:29:54 PDT
Created attachment 401637 [details] Patch This is the solution I like most. Added an Optional parameter to the LazilyInitialized class to be able to provide a deinitializer. I think code looks nice and smooth now.
Xabier Rodríguez Calvar
Comment 18 2020-06-11 07:04:44 PDT
Created attachment 401639 [details] Patch And now with the test for the deinitializer and Apple build fix (I hope).
Xabier Rodríguez Calvar
Comment 19 2020-06-12 01:19:40 PDT
Created attachment 401713 [details] Patch I think this is now ready for final review and landing
youenn fablet
Comment 20 2020-06-12 01:49:37 PDT
Comment on attachment 401713 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=401713&action=review > Source/WTF/wtf/LazilyInitialized.h:51 > + , m_deinitializer(WTFMove(deinitializer)) { } I am not really convinced by LazilyInitialized. I think it would be best to introduce it as a separate follow-up patch. And keep in this patch the usual approach of a class wrapper or a CDMProxyClearKey getter like: CDMProxyClearKey::cryptHandle() { if (!m_handleInitialized) { m_handleInitialized = true; initializeGcrypt(); } return m_gcryHandle; } > Source/WTF/wtf/LazilyInitialized.h:75 > + if (!m_isInitialized.exchange(true)) This is probably not thread safe. If two threads call operator*() at the same time, there is a risk that m_value is used by one of the thread without being initialized. > Source/WebCore/platform/encryptedmedia/CDMProxy.cpp:43 > + static NeverDestroyed<Vector<CDMProxyFactory*>> factories; Maybe this could be a WeakHashSet to prevent UAF. If this is supposed to be keyed by keySystem, maybe it should be a HashMap? > Source/WebCore/platform/encryptedmedia/CDMProxy.cpp:52 > +void CDMProxyFactory::registerFactory(CDMProxyFactory& factory) It seems these methods are not called in this patch. It would be safer to register/unregister in CDMProxyFactory constructor/destructor if possible.
Xabier Rodríguez Calvar
Comment 21 2020-06-12 05:53:58 PDT
(In reply to youenn fablet from comment #20) > Comment on attachment 401713 [details] > Patch > > View in context: > https://bugs.webkit.org/attachment.cgi?id=401713&action=review > > > Source/WTF/wtf/LazilyInitialized.h:51 > > + , m_deinitializer(WTFMove(deinitializer)) { } > > I am not really convinced by LazilyInitialized. > I think it would be best to introduce it as a separate follow-up patch. > And keep in this patch the usual approach of a class wrapper or a > CDMProxyClearKey getter like: > CDMProxyClearKey::cryptHandle() > { > if (!m_handleInitialized) { > m_handleInitialized = true; > initializeGcrypt(); > } > return m_gcryHandle; > } If you have a strong opinion I'll do it, but I don't agree. I think this is a good solution. > > Source/WTF/wtf/LazilyInitialized.h:75 > > + if (!m_isInitialized.exchange(true)) > > This is probably not thread safe. If two threads call operator*() at the > same time, there is a risk that m_value is used by one of the thread without > being initialized. You're right. Maybe we can forget about thread safety and make it just a bool. > > Source/WebCore/platform/encryptedmedia/CDMProxy.cpp:43 > > + static NeverDestroyed<Vector<CDMProxyFactory*>> factories; > > Maybe this could be a WeakHashSet to prevent UAF. > If this is supposed to be keyed by keySystem, maybe it should be a HashMap? It could but I don't follow why it should be a WeahHashSet. Who would own the factories then? > > Source/WebCore/platform/encryptedmedia/CDMProxy.cpp:52 > > +void CDMProxyFactory::registerFactory(CDMProxyFactory& factory) > > It seems these methods are not called in this patch. > It would be safer to register/unregister in CDMProxyFactory > constructor/destructor if possible. They are called for GStreamer.
Xabier Rodríguez Calvar
Comment 22 2020-06-15 08:07:58 PDT
Created attachment 401897 [details] Patch (In reply to Xabier Rodríguez Calvar from comment #21) > > > Source/WTF/wtf/LazilyInitialized.h:51 > > > + , m_deinitializer(WTFMove(deinitializer)) { } > > > > I am not really convinced by LazilyInitialized. > > I think it would be best to introduce it as a separate follow-up patch. > > And keep in this patch the usual approach of a class wrapper or a > > CDMProxyClearKey getter like: > > CDMProxyClearKey::cryptHandle() > > { > > if (!m_handleInitialized) { > > m_handleInitialized = true; > > initializeGcrypt(); > > } > > return m_gcryHandle; > > } > > If you have a strong opinion I'll do it, but I don't agree. I think this is > a good solution. When I think LazilyInitialized is a more elegant solution, I refactored as you wanted. > > > Source/WebCore/platform/encryptedmedia/CDMProxy.cpp:43 > > > + static NeverDestroyed<Vector<CDMProxyFactory*>> factories; > > > > Maybe this could be a WeakHashSet to prevent UAF. > > If this is supposed to be keyed by keySystem, maybe it should be a HashMap? > > It could but I don't follow why it should be a WeahHashSet. Who would own > the factories then? Finally, this can't be done. Things can't be keyed by key system because a factory can support more than one system (well, it could be but I think the alternative of adding the same factory more than once would be worse than this). > > > Source/WebCore/platform/encryptedmedia/CDMProxy.cpp:52 > > > +void CDMProxyFactory::registerFactory(CDMProxyFactory& factory) > > > > It seems these methods are not called in this patch. > > It would be safer to register/unregister in CDMProxyFactory > > constructor/destructor if possible. > > They are called for GStreamer. I think now we would be ready for landing if you agree.
youenn fablet
Comment 23 2020-06-15 08:41:15 PDT
Comment on attachment 401897 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=401897&action=review > Source/WebCore/platform/encryptedmedia/CDMProxy.h:161 > + virtual ~CDMProxyFactory() { }; We could add an ASSERT that the factory is not contained in the list of factories. Or call unregisterFactory. > Source/WebCore/platform/encryptedmedia/CDMProxy.h:169 > + WEBCORE_EXPORT static void platformRegisterFactories(Vector<CDMProxyFactory*>&); Should we make it private then? Maybe rename it to initializeRegisteredFactories(). Also we do not really like out parameters. We could change it to return a Vector<> that we would move into NeverDestroyed<Vector<CDMProxyFactory*>> factories. > Source/WebCore/platform/encryptedmedia/CDMProxy.h:186 > + CDMInstanceProxy(const String& keySystem) explicit > Source/WebCore/platform/encryptedmedia/CDMProxy.h:-177 > - m_cdmProxy->updateKeyStore(m_keyStore); We remove that code, is it ok? > Source/WebCore/platform/graphics/gstreamer/eme/CDMProxyClearKey.cpp:222 > + m_isGCryptInitialized = true; This is very similar to an Optional. I would make m_gCryptHandle an Optional so that we are sure we never use it uninitialised. > Source/WebCore/platform/graphics/gstreamer/eme/CDMProxyClearKey.h:48 > + virtual ~CDMProxyFactoryClearKey(); No need for virtual here. > Source/WebCore/platform/graphics/gstreamer/eme/CDMProxyClearKey.h:51 > + bool supportsKeySystem(const String&) final; Can we make them private?
Xabier Rodríguez Calvar
Comment 24 2020-06-16 07:34:21 PDT
Created attachment 402003 [details] Patch (In reply to youenn fablet from comment #23) > > Source/WebCore/platform/encryptedmedia/CDMProxy.h:161 > > + virtual ~CDMProxyFactory() { }; > > We could add an ASSERT that the factory is not contained in the list of > factories. > Or call unregisterFactory. Done. > > Source/WebCore/platform/encryptedmedia/CDMProxy.h:169 > > + WEBCORE_EXPORT static void platformRegisterFactories(Vector<CDMProxyFactory*>&); > > Should we make it private then? > Maybe rename it to initializeRegisteredFactories(). > Also we do not really like out parameters. > We could change it to return a Vector<> that we would move into > NeverDestroyed<Vector<CDMProxyFactory*>> factories. I changed everything but the name. I really think current one is better than initializeRegisteredFactories, since it is platform related and it is actually what registers the factories, they are not registered yet. > > Source/WebCore/platform/encryptedmedia/CDMProxy.h:186 > > + CDMInstanceProxy(const String& keySystem) > > explicit Done. > > Source/WebCore/platform/encryptedmedia/CDMProxy.h:-177 > > - m_cdmProxy->updateKeyStore(m_keyStore); > > We remove that code, is it ok? Yes, because this was necessary when we were setting the proxy later. Now that it's created at the same time than the instance, there is no need for this. This was a Charlie's concern as well but the tests he mentions are not flaky at all with this code. > > Source/WebCore/platform/graphics/gstreamer/eme/CDMProxyClearKey.cpp:222 > > + m_isGCryptInitialized = true; > > This is very similar to an Optional. > I would make m_gCryptHandle an Optional so that we are sure we never use it > uninitialised. Done. > > Source/WebCore/platform/graphics/gstreamer/eme/CDMProxyClearKey.h:48 > > + virtual ~CDMProxyFactoryClearKey(); > > No need for virtual here. Done. > > Source/WebCore/platform/graphics/gstreamer/eme/CDMProxyClearKey.h:51 > > + bool supportsKeySystem(const String&) final; > > Can we make them private? Done.
EWS
Comment 25 2020-06-16 07:57:28 PDT
Committed r263087: <https://trac.webkit.org/changeset/263087> All reviewed patches have been landed. Closing bug and clearing flags on attachment 402003 [details].
Radar WebKit Bug Importer
Comment 26 2020-06-16 07:58:17 PDT
Radar WebKit Bug Importer
Comment 27 2020-06-16 07:58:20 PDT
Note You need to log in before you can comment on or make changes to this bug.