www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Greenwashing: best practices

reply Johannes T <isrvoid gmail.com> writes:
I was searching for ways to mitigate DIP's 1028 greenwashing 
concerns without resorting to  safe extern. It turns out, D 
already has tools to address that. It's a matter of recognizing 
and promoting best practices. Allow me to elaborate.

Verified C functions require  trusted declaration when used in 
safe code. It's a slight antipattern, similar to a bad comment:
// implementation looks safe
ssize_t recv(void*, size_t);

Yes,  trusted is parsed and can be searched, that's not the 
point. It's too far from the source code. What version was 
vetted? Are we linking against an older one that leaked? Hard to 
say without checking. This leads to the first advice:

Declarations should reside in .di files and not be scattered 
throughout D code. It allows us to keep .di closer to the library 
and version it separately. If the version changes, there is no 
need to search for affected D sources.

Many bindings already do that. I wasn't aware of it and just used 
extern(C) as needed.

Let's assume we've created a .di file with some internal C 
backend stuff. Trying to call it doesn't compile. The next advice 
could be:

To force unchecked functions to compile, the corresponding 
declarations should be surrounded by  trusted { } block. The 
block suggests it was rubber-stamped without audit. It's also the 
path of least resistance for multiple functions.  trusted should 
only be added to a single declaration if it was verified.

I imagine being a C++ programmer trying out D and don't see much 
inconvenience following this. More importantly, when the program 
crashes, I would remember adding  trusted { } and can't blame D's 
safety annotation.
May 30 2020
next sibling parent reply Paul Backus <snarwin gmail.com> writes:
On Saturday, 30 May 2020 at 21:31:11 UTC, Johannes T wrote:
 Verified C functions require  trusted declaration when used in 
 safe code. It's a slight antipattern, similar to a bad comment:
 // implementation looks safe
 ssize_t recv(void*, size_t);

 Yes,  trusted is parsed and can be searched, that's not the 
 point. It's too far from the source code. What version was 
 vetted? Are we linking against an older one that leaked? Hard 
 to say without checking.
Realistically, if you're writing portable code, the version that's vetted is the version described by the documentation or standard that describes the interface you're programming to. So in this case, you'd refer to the POSIX standard's page on `recv`: https://pubs.opengroup.org/onlinepubs/9699919799/functions/recv.html If a particular version of a particular implementation on a particular platform has a bug, well, that's the implementation's problem, not yours. You can't possibly vet every version of `recv` that your code could ever be linked with (especially because some of those versions may not even exist at the time you do the vetting!).
 Declarations should reside in .di files and not be scattered 
 throughout D code. It allows us to keep .di closer to the 
 library and version it separately. If the version changes, 
 there is no need to search for affected D sources.

 Many bindings already do that. I wasn't aware of it and just 
 used extern(C) as needed.
As far as I know there's no advantage to using a .di file over a regular .d file for C bindings. It *is* a good idea to put translated C headers in their own files, rather than sprinkling declarations around willy-nilly, for the same reason it's good to put your C declarations in .h files, rather than sprinkling them around willy-nilly.
 To force unchecked functions to compile, the corresponding 
 declarations should be surrounded by  trusted { } block. The 
 block suggests it was rubber-stamped without audit. It's also 
 the path of least resistance for multiple functions.  trusted 
 should only be added to a single declaration if it was verified.

 I imagine being a C++ programmer trying out D and don't see 
 much inconvenience following this. More importantly, when the 
 program crashes, I would remember adding  trusted { } and can't 
 blame D's safety annotation.
Sure, I guess you could do this if you wanted to prototype something quickly and didn't care much about making sure it was 100% memory-safe. On the other hand, you could *also* just write the whole thing as system, and leave migrating to safe for later--at which point the compiler will bug you about your C function calls, and you can take the time to fix them *properly*. This has the advantage that you never give yourself a false sense of security by pretending code is safe when it actually isn't.
May 30 2020
parent Johannes T <isrvoid gmail.com> writes:
On Saturday, 30 May 2020 at 22:00:27 UTC, Paul Backus wrote:
 [..]
 If a particular version of a particular implementation on a 
 particular platform has a bug, well, that's the 
 implementation's problem, not yours. You can't possibly vet 
 every version of `recv` that your code could ever be linked 
 with (especially because some of those versions may not even 
 exist at the time you do the vetting!).
 [..]
That's a good point. Also, when an issue is discovered in a newer release, changing back to system wouldn't be a sensible path. The safety of the interface should govern the attribute. I didn't realize that.
 [..]
 As far as I know there's no advantage to using a .di file over 
 a regular .d file for C bindings.
 [..]
You're right, .di shouldn't cause contention. Use whatever is more consistent.
 [..]
 On the other hand, you could *also* just write the whole thing 
 as  system, and leave migrating to  safe for later--at which 
 point the compiler will bug you about your C function calls, 
 and you can take the time to fix them *properly*. This has the 
 advantage that you never give yourself a false sense of 
 security by pretending code is  safe when it actually isn't.
I hope Nick Treleaven's great idea with ` safe module foo;` will gain traction. It would make the migration path more convenient. Thanks!
May 31 2020
prev sibling parent reply ag0aep6g <anonymous example.com> writes:
On 30.05.20 23:31, Johannes T wrote:
 To force unchecked functions to compile, the corresponding declarations 
 should be surrounded by  trusted { } block. The block suggests it was 
 rubber-stamped without audit. It's also the path of least resistance for 
 multiple functions.  trusted should only be added to a single 
 declaration if it was verified.
I think it would be a mistake to try and distinguish ` trusted` from ` trusted { ... }` that way. A UDA (.e.g ` audited`) would be a better fit if you want to communicate that an audit has happened. As far as I'm aware, there is generally no expectation of trusted extern functions having their implementations verified. First and foremost, trusted marks a safe interface [1]. Whether the implementation is bug-free is secondary. On a D function, trusted is also just an "I think it's safe" comment by the author. It doesn't indicate that the code was audited by anyone else. [1] https://dlang.org/spec/function.html#safe-interfaces
May 30 2020
parent reply Johannes T <isrvoid gmail.com> writes:
On Saturday, 30 May 2020 at 22:01:14 UTC, ag0aep6g wrote:
 On 30.05.20 23:31, Johannes T wrote:
 To force unchecked functions to compile, the corresponding 
 declarations should be surrounded by  trusted { } block. The 
 block suggests it was rubber-stamped without audit. It's also 
 the path of least resistance for multiple functions.  trusted 
 should only be added to a single declaration if it was 
 verified.
I think it would be a mistake to try and distinguish ` trusted` from ` trusted { ... }` that way. A UDA (.e.g ` audited`) would be a better fit if you want to communicate that an audit has happened.
You're right. It does seem bad and shouldn't be promoted. I think there is still some value using it as a smell. If you see trusted: or trusted { }, it's probably there to shut up the compiler.
 As far as I'm aware, there is generally no expectation of 
  trusted extern functions having their implementations 
 verified. First and foremost,  trusted marks a safe interface 
 [1]. Whether the implementation is bug-free is secondary.

 On a D function,  trusted is also just an "I think it's safe" 
 comment by the author. It doesn't indicate that the code was 
 audited by anyone else.

 [1] https://dlang.org/spec/function.html#safe-interfaces
That's an important distinction. I didn't realize it until recently. Thank you.
May 31 2020
parent ag0aep6g <anonymous example.com> writes:
On 31.05.20 13:08, Johannes T wrote:
 I think there is still some value using it as a smell. If you see
  trusted: or  trusted { }, it's probably there to shut up the
 compiler.
Agreed.
May 31 2020