We plan to move most of the contents of the "raw_ptr<T> Usage Rules" doc into //base/memory/raw_ptr.md, but we think that the following high-level rules should become part of the Chromium C++ Style Guide:raw_ptr<T> is a drop-in replacement for raw pointers. It behaves just like a raw pointer with an exception that it is zero-initialized and cleared on destruction and move. Unlike std::unique_ptr<T>, base::scoped_refptr<T>, etc., it doesn’t manage ownership or lifetime of an allocated object - you are still responsible for freeing the object when no longer used, just as you would with a raw pointer.
raw_ptr<T> prevents some Use-after-Free (UaF) bugs from being exploitable (by poisoning the freed memory and quarantining it as long as a dangling raw_ptr<T> exists), but dereferencing a raw_ptr<T> to freed memory may still crash.
Initialization, assignment, and destruction of a raw_ptr<T> incur additional performance overhead compared to a raw pointer. There is no overhead when dereferencing a pointer, however.
In general, raw pointers should be avoided and a smart pointer with the right ownership semantics should be used instead. Google C++ Style Guide further clarifies to prefer unique ownership (i.e. std::unique_ptr) over shared ownership (i.e. scoped_refptr or base::WeakPtr). For non-owning pointers, Chromium provides raw_ptr<T> as a security-beneficial alternative to raw T* pointers. When to use raw_ptr<T> is described in the high level guidelines below.
Use raw_ptr<T> instead of a raw T* pointer in every class/struct field, unless:
The pointer has type |const char*|, is a pointer to an Objective-C object, or is a function pointer.
The field is defined in a source file listed in .../raw_ptr_exception_paths.txt. Acceptable exclusions cover:
Renderer-only code.
Code that cannot depend on //base.
Code in //ppapi.
The field is annotated with the RAW_PTR_PLUGIN_IGNORE attribute. Acceptable exclusions cover:
All of its possible values point outside of PartitionAlloc (e.g. literals, stack allocated memory, shared memory, mmap'ed memory, V8/Oilpan/Java heaps, TLS, etc.).
It doesn’t compile (when it happens, consult Recoverable compile-time problems and You likely won’t be able to make it compile first).
[very rare] It causes regression on perf bots.
[very rare] It leads to runtime errors (i.e. when following the Extra pointer rules is not possible).
The rules above are *not* enforced yet - before we confirm raw_ptr’s performance characteristics through Stable channel experiments (in the longer term violating the rules will lead to compile errors through Chromium Clang Plugin). For more details, please refer to //base/memory/raw_ptr.md.
Hello c...@chromium.org,Based on recent experiments, the go/miracleptr project is quite close to being ready for the Big Rewrite, where we will rewrite `SomeClass* field_` fields into `raw_ptr<SomeClass> field_`. While we prepare for this rewrite (including drafting a broader PSA + scheduling a tree closure), it seems useful to start discussing how the Chromium C++ Style Guide can accommodate `raw_ptr<T>`.Before I dive deeper into details, let me summarize what raw_ptr<T> is (copy&pasting from the "raw_ptr<T> Usage Rules" doc):We plan to move most of the contents of the "raw_ptr<T> Usage Rules" doc into //base/memory/raw_ptr.md, but we think that the following high-level rules should become part of the Chromium C++ Style Guide:raw_ptr<T> is a drop-in replacement for raw pointers. It behaves just like a raw pointer with an exception that it is zero-initialized and cleared on destruction and move. Unlike std::unique_ptr<T>, base::scoped_refptr<T>, etc., it doesn’t manage ownership or lifetime of an allocated object - you are still responsible for freeing the object when no longer used, just as you would with a raw pointer.
raw_ptr<T> prevents some Use-after-Free (UaF) bugs from being exploitable (by poisoning the freed memory and quarantining it as long as a dangling raw_ptr<T> exists), but dereferencing a raw_ptr<T> to freed memory may still crash.
Initialization, assignment, and destruction of a raw_ptr<T> incur additional performance overhead compared to a raw pointer. There is no overhead when dereferencing a pointer, however.In general, raw pointers should be avoided and a smart pointer with the right ownership semantics should be used instead. Google C++ Style Guide further clarifies to prefer unique ownership (i.e. std::unique_ptr) over shared ownership (i.e. scoped_refptr or base::WeakPtr).
For non-owning pointers, Chromium provides raw_ptr<T> as a security-beneficial alternative to raw T* pointers. When to use raw_ptr<T> is described in the high level guidelines below.
Use raw_ptr<T> instead of a raw T* pointer in every class/struct field, unless:
The pointer has type |const char*|
, is a pointer to an Objective-C object, or is a function pointer.
The field is defined in a source file listed in .../raw_ptr_exception_paths.txt. Acceptable exclusions cover:
Renderer-only code.
Code that cannot depend on //base.
Code in //ppapi.
The field is annotated with the RAW_PTR_PLUGIN_IGNORE attribute. Acceptable exclusions cover:
All of its possible values point outside of PartitionAlloc (e.g. literals, stack allocated memory, shared memory, mmap'ed memory, V8/Oilpan/Java heaps, TLS, etc.).
It doesn’t compile (when it happens, consult Recoverable compile-time problems and You likely won’t be able to make it compile first).
[very rare] It causes regression on perf bots.
[very rare] It leads to runtime errors (i.e. when following the Extra pointer rules is not possible).
The rules above are *not* enforced yet - before we confirm raw_ptr’s performance characteristics through Stable channel experiments (in the longer term violating the rules will lead to compile errors through Chromium Clang Plugin). For more details, please refer to //base/memory/raw_ptr.md.Do you have any feedback or comments on the proposal above? Do you think the raw_ptr<T> rules above can be covered in a subsection of //styleguide/c++/c++.md (maybe next to the "Object ownership and calling conventions" section)?
Or maybe you'd suggest covering the rules above in a separate file/document under //styleguide/c++?Thanks,Lukasz
--
You received this message because you are subscribed to the Google Groups "cxx" group.
To unsubscribe from this group and stop receiving emails from it, send an email to cxx+uns...@chromium.org.
To view this discussion on the web visit https://groups.google.com/a/chromium.org/d/msgid/cxx/CAA_NCUEaAEqtWs5D0a_cPL%2Be3dgcFJ-4wLTMfEHifTpybzGJVw%40mail.gmail.com.
I think it would be useful to talk about when to use raw_ptr vs WeakPtr.
Do you mean, the pointer is to a static string? The rewriter can't tell but humans can.
Should we move raw_ptr out of base then? What can't dep on base?
I would say that a clang plugin will enforce this in the future, but that the RAW_PTR_PLUGIN_IGNORE attribute can be used to override it.
All of its possible values point outside of PartitionAlloc (e.g. literals, stack allocated memory, shared memory, mmap'ed memory, V8/Oilpan/Java heaps, TLS, etc.).
This is hard to verify, would we really care to annotate it? Then if it changes we wouldn't know. Why not leave it as raw_ptr?
Thanks Dana!May I suggest moving the discussion about exact text into https://docs.google.com/document/d/1fdYDDf5kSRAFKJF5Omp1xCKzlhycV1jKwaCkJPGGK7s/edit#heading=h.jubgcrks1vchBelow my responses to your suggestions so far.I think it would be useful to talk about when to use raw_ptr vs WeakPtr.We want to make the rule extremely simple, and prefer not to go beyond "use raw_ptr<> where you'd use raw pointer" here.Do you mean, the pointer is to a static string? The rewriter can't tell but humans can.We decided to not deal with |const char*| in v1. If we can't rewrite, we can't enforce, so decided not to burden devs with this decision for now. Not to mention it'd be easy to get it wrong. We'll tackle this once we have StringLiteralPtr, which will make it harder to make a mistake.Should we move raw_ptr out of base then? What can't dep on base?I would say that a clang plugin will enforce this in the future, but that the RAW_PTR_PLUGIN_IGNORE attribute can be used to override it.It'll kinda divert from the "use raw_ptr<T> unless" flow.All of its possible values point outside of PartitionAlloc (e.g. literals, stack allocated memory, shared memory, mmap'ed memory, V8/Oilpan/Java heaps, TLS, etc.).This is hard to verify, would we really care to annotate it? Then if it changes we wouldn't know. Why not leave it as raw_ptr?For perf reasons.
To view this discussion on the web visit https://groups.google.com/a/chromium.org/d/msgid/cxx/CA%2B%2B_KbScXqBuDSAiEkb_%2BZE1c2%2BNEoGnXEncYecsS1n%2BLNwRVg%40mail.gmail.com.
On Tue, Nov 16, 2021 at 7:04 PM Bartek Nowierski <bar...@chromium.org> wrote:Thanks Dana!May I suggest moving the discussion about exact text into https://docs.google.com/document/d/1fdYDDf5kSRAFKJF5Omp1xCKzlhycV1jKwaCkJPGGK7s/edit#heading=h.jubgcrks1vchBelow my responses to your suggestions so far.I think it would be useful to talk about when to use raw_ptr vs WeakPtr.We want to make the rule extremely simple, and prefer not to go beyond "use raw_ptr<> where you'd use raw pointer" here.Do you mean, the pointer is to a static string? The rewriter can't tell but humans can.We decided to not deal with |const char*| in v1. If we can't rewrite, we can't enforce, so decided not to burden devs with this decision for now. Not to mention it'd be easy to get it wrong. We'll tackle this once we have StringLiteralPtr, which will make it harder to make a mistake.Should we move raw_ptr out of base then? What can't dep on base?I would say that a clang plugin will enforce this in the future, but that the RAW_PTR_PLUGIN_IGNORE attribute can be used to override it.It'll kinda divert from the "use raw_ptr<T> unless" flow.All of its possible values point outside of PartitionAlloc (e.g. literals, stack allocated memory, shared memory, mmap'ed memory, V8/Oilpan/Java heaps, TLS, etc.).This is hard to verify, would we really care to annotate it? Then if it changes we wouldn't know. Why not leave it as raw_ptr?For perf reasons.I think high level what we're not agreeing on is:- These rules seem to match what the rewriter did. And that makes sense - it's a mass rewrite of all our existing code, without any human thought into each case.
- What I believe the style guide is for is for people writing or editing code by hand, where there is a lot more context and understanding, and it's a single pointer at a time.So I don't really believe these need to be the same, and we should error toward simpler/fewer rules in the style guide. People will and can always make exceptions when they must for some perf critical case or something. But we should give the guidance for what we expect.So for example storing a pointer T* on a class, that happens to be to stack memory - AutoReset perhaps is an example of this. Sometimes it may be on the stack, in which case raw_ptr<T> is useless. But over time that may change and it may hold a non-stack pointer too. So it's much safer and simpler to say all raw pointer fields you write are raw_ptr<T> and then maybe the runtime effect is nothing, and maybe in new usage it does something, or maybe raw_ptr evolves and it does something. But the rule is simple.Do you agree on this higher level direction for advice?
I think high level what we're not agreeing on is:
- These rules seem to match what the rewriter did. And that makes sense - it's a mass rewrite of all our existing code, without any human thought into each case.
- What I believe the style guide is for is for people writing or editing code by hand, where there is a lot more context and understanding, and it's a single pointer at a time.
I think high level what we're not agreeing on is:
- These rules seem to match what the rewriter did. And that makes sense - it's a mass rewrite of all our existing code, without any human thought into each case.
- What I believe the style guide is for is for people writing or editing code by hand, where there is a lot more context and understanding, and it's a single pointer at a time.I appreciate your desire to increase the scope of protection. We have the same goal. Please keep in mind a couple things:1. If we can't rewrite something automatically, we can't enforce it (with exception of a small number of classes of exceptions that are well contained and we can handle manually after Big Rewrite).
2. Certain classes of exceptions (e.g. Renderer-only code, const char*, pointers in templated collections/wrappers) will be evaluated as a follow-up project and better to not touch them yet.