digitalmars.D - What exactly does " safe" mean?
- monarch_dodra (23/23) Jun 01 2013 The way I understood it, @safe defines a list of things that are
- Nick Sabalausky (5/33) Jun 01 2013 Core dumps aren't the big problem @safe tries to avoid. The big problem
- monarch_dodra (14/50) Jun 01 2013 So, let's say I have:
- Maxim Fomin (32/47) Jun 01 2013 I don't see how this is related to @safe. Remove the attribute,
- Maxim Fomin (25/49) Jun 01 2013 You completely get the point. Safe attribute does not guarantee
- Jonathan M Davis (22/50) Jun 01 2013 @safe is for memory safety, meaning that @safe code cannot corrupt memor...
- Peter Alexander (25/34) Jun 01 2013 Not true.
- Jonathan M Davis (8/48) Jun 01 2013 They're guaranteed to not introduce any such behavior. They can't possib...
- Maxim Fomin (21/34) Jun 01 2013 Updated example from above to show how @safe can introduce UB.
- Peter Alexander (6/12) Jun 01 2013 I believe that's a compiler bug.
- Jonathan M Davis (24/64) Jun 01 2013 That's a known bug in @safe. Slicing a static array should be considered...
- Piotr Szturmaj (22/32) Jun 01 2013 I know that introducing another type qualifier may complicate things
- Jonathan M Davis (45/90) Jun 01 2013 Except that that makes it so that you can't return a ref argument, which...
- Paulo Pinto (10/44) Jun 01 2013 I always assumed that the role of @safe is to behave like safe code in
- monarch_dodra (25/102) Jun 01 2013 OK. But by that standard, can't (mostly) anything be trusted?
- Jonathan M Davis (20/121) Jun 01 2013 When a programmer marks a function as @trusted, they are saying that the...
- monarch_dodra (10/42) Jun 02 2013 Well, by "mostly", I did mean stuff that's not blatantly wrong. I
- Jonathan M Davis (14/31) Jun 02 2013 I don't know. The function isn't doing anything unsafe in and of itself....
- Steven Schveighoffer (14/23) Jun 03 2013 provable @safe depends on the precondition that its parameters are valid...
- Jonathan M Davis (3/6) Jun 03 2013 Nice analogy.
- Andrei Alexandrescu (5/7) Jun 01 2013 If you call the function from a program with memory integrity and it
- monarch_dodra (9/18) Jun 01 2013 OK. In a word, I guess that makes sense. I'll stick to that
- Jonathan M Davis (4/29) Jun 01 2013 Because the safety of the function depends on the caller, I'd say that i...
The way I understood it, safe defines a list of things that are or aren't legal inside the implementation of a function. It also changes the scheme of bounds checking, in release code. What bothers me though, is that from an interface point of view, it doesn't really mean anything (or at least, I haven't really understood anything). AFAIK: if I call something " safe", chances of a core dump are relatively "lower", but they can still happen: * A function that accepts a pointer as an argument can be marked safe, so all bets are off there, no, since the pointer can be dereferenced? * Member functions for structs that have pointers, too, can be marked safe... Or does it only mean "if you give me valid pointers, I can't core dump*"? (*ignoring current flaws, such as escaping slices from static arrays) The main reason about this question is that now I'm confused about trusted: what are the conditions a developer needs to take into account before marking a function " trusted" ? Ditto for member functions, when they operate on pointer members. Can those be safe? Yeah, overall, I'm confused as to what " safe" means from an interface point of view :(
Jun 01 2013
On Sat, 01 Jun 2013 21:59:18 +0200 "monarch_dodra" <monarchdodra gmail.com> wrote:The way I understood it, safe defines a list of things that are or aren't legal inside the implementation of a function. It also changes the scheme of bounds checking, in release code. What bothers me though, is that from an interface point of view, it doesn't really mean anything (or at least, I haven't really understood anything). AFAIK: if I call something " safe", chances of a core dump are relatively "lower", but they can still happen: * A function that accepts a pointer as an argument can be marked safe, so all bets are off there, no, since the pointer can be dereferenced? * Member functions for structs that have pointers, too, can be marked safe... Or does it only mean "if you give me valid pointers, I can't core dump*"? (*ignoring current flaws, such as escaping slices from static arrays) The main reason about this question is that now I'm confused about trusted: what are the conditions a developer needs to take into account before marking a function " trusted" ? Ditto for member functions, when they operate on pointer members. Can those be safe? Yeah, overall, I'm confused as to what " safe" means from an interface point of view :(Core dumps aren't the big problem safe tries to avoid. The big problem is memory corruption, ie trampling memory you didn't expect to (or shouldn't be allowed to).
Jun 01 2013
On Saturday, 1 June 2013 at 20:22:01 UTC, Nick Sabalausky wrote:On Sat, 01 Jun 2013 21:59:18 +0200 "monarch_dodra" <monarchdodra gmail.com> wrote:So, let's say I have: -------- void foo(int* p) safe { *p = 0; } -------- I suppose that this give foo the liberty of saying "p points to someplace valid" ... "and if not, it's not my fault"? I suppose something that is trusted then means "I will not trample your memory under any circumstance, even if I'm doing unsafe things under the hood (unless you give a pointer that is already bad)"?The way I understood it, safe defines a list of things that are or aren't legal inside the implementation of a function. It also changes the scheme of bounds checking, in release code. What bothers me though, is that from an interface point of view, it doesn't really mean anything (or at least, I haven't really understood anything). AFAIK: if I call something " safe", chances of a core dump are relatively "lower", but they can still happen: * A function that accepts a pointer as an argument can be marked safe, so all bets are off there, no, since the pointer can be dereferenced? * Member functions for structs that have pointers, too, can be marked safe... Or does it only mean "if you give me valid pointers, I can't core dump*"? (*ignoring current flaws, such as escaping slices from static arrays) The main reason about this question is that now I'm confused about trusted: what are the conditions a developer needs to take into account before marking a function " trusted" ? Ditto for member functions, when they operate on pointer members. Can those be safe? Yeah, overall, I'm confused as to what " safe" means from an interface point of view :(Core dumps aren't the big problem safe tries to avoid. The big problem is memory corruption, ie trampling memory you didn't expect to (or shouldn't be allowed to).
Jun 01 2013
On Saturday, 1 June 2013 at 20:31:45 UTC, monarch_dodra wrote:On Saturday, 1 June 2013 at 20:22:01 UTC, Nick Sabalausky wrote:I don't see how this is related to safe. Remove the attribute, and you are in same situation.Core dumps aren't the big problem safe tries to avoid. The big problem is memory corruption, ie trampling memory you didn't expect to (or shouldn't be allowed to).So, let's say I have: -------- void foo(int* p) safe { *p = 0; } --------I suppose that this give foo the liberty of saying "p points to someplace valid" ... "and if not, it's not my fault"?I think in D any reference or pointer may refer to anywhere. Actually, example above is not a big deal. Example of bigger problem: extern(C) int printf(const char*,...) safe; alias int T; auto foo(lazy T i) safe { return { return i; } ; } auto bar() safe { T i = 4; return foo(i); } void baz() safe { double[2] i = 3.14; } void main() safe { auto x = bar(); baz(); printf("%d\n", x()); } This may print garbage or not, it also can print constant garbage or changing, depending on compiler switches. Assume that foo, bar and baz are splitted in several modules and instead of int there is more complex data structure. This is example of memory error not prevented by safe.
Jun 01 2013
On Saturday, 1 June 2013 at 19:59:19 UTC, monarch_dodra wrote:The way I understood it, safe defines a list of things that are or aren't legal inside the implementation of a function. It also changes the scheme of bounds checking, in release code.You completely get the point. Safe attribute does not guarantee safety, but blocks some practices which are supposed to be unsafe. Such characteristic raises several questions: 1) How strong are safe commitments? I value them quite weak, because safe function can call trusted and systems function indirectly. 2) How (un)safe are those practices which are still allowed? They are also unsafe and issue here is not trivial cases like dereferencing null. Current type system has some holes, so any practice blocked in safe can be achieved by employing type system holes (delegates, ref and lazy parameters). On the other hand, there is no point in using these holes - just do not make a function safe if it cannot be. However there is still a problem of doing unintentional (especially memory) errors.What bothers me though, is that from an interface point of view, it doesn't really mean anything (or at least, I haven't really understood anything). AFAIK: if I call something " safe", chances of a core dump are relatively "lower", but they can still happen: * A function that accepts a pointer as an argument can be marked safe, so all bets are off there, no, since the pointer can be dereferenced? * Member functions for structs that have pointers, too, can be marked safe...Yes, safe is not an interface feature (in a sense of delivering some guarantee). As it stands, it is a commitment to not to do some potentially unsafe actions. It is useful only in cases when user doesn't want do unsafe things and asks compiler for help in detecting some of them.Or does it only mean "if you give me valid pointers, I can't core dump*"? (*ignoring current flaws, such as escaping slices from static arrays)Of course not.The main reason about this question is that now I'm confused about trusted: what are the conditions a developer needs to take into account before marking a function " trusted" ?I think it is the same except what is listed in the spec. Nothing conceptually different.Ditto for member functions, when they operate on pointer members. Can those be safe?What do you mean?Yeah, overall, I'm confused as to what " safe" means from an interface point of view :(
Jun 01 2013
On Saturday, June 01, 2013 21:59:18 monarch_dodra wrote:The way I understood it, safe defines a list of things that are or aren't legal inside the implementation of a function. It also changes the scheme of bounds checking, in release code. What bothers me though, is that from an interface point of view, it doesn't really mean anything (or at least, I haven't really understood anything). AFAIK: if I call something " safe", chances of a core dump are relatively "lower", but they can still happen: * A function that accepts a pointer as an argument can be marked safe, so all bets are off there, no, since the pointer can be dereferenced? * Member functions for structs that have pointers, too, can be marked safe... Or does it only mean "if you give me valid pointers, I can't core dump*"? (*ignoring current flaws, such as escaping slices from static arrays) The main reason about this question is that now I'm confused about trusted: what are the conditions a developer needs to take into account before marking a function " trusted" ? Ditto for member functions, when they operate on pointer members. Can those be safe? Yeah, overall, I'm confused as to what " safe" means from an interface point of view :(safe is for memory safety, meaning that safe code cannot corrupt memory. You can get segfaults due to null pointers and the like, but you can't have code which writes passed the end of a buffer, or which uses a freed memory, or does anything else which involves writing or reading from memory which variables aren't supposed to have access to. Assuming that there are no bugs in safe, the one thing that can invalidate it is trusted. With trusted code, it is the _programmer_ who is then guaranteeing that the code is actually safe. The code is doing something which is potentially not safe (and therefore is considered system by the compiler) but which _could_ be safe if the code is correct, and if the programmer is marking the code as trusted, they are then telling the compiler that they've verified that the code isn't doing anything which could corrupt memory. As long as the programmer doesn't screw that up, then any safe code calling that trusted function is indeed safe, but if the programmer screwed it up, then you could still get memory corruption. However, here's really no way to get around that problem with a systems language, since most code needs to eventually call something that's system (e.g. all I/O needs system stuff internally). But by limiting how much code is system or trusted, most code is safe with a minimal amount of code having to have been verified by an appropriately competent programmer as being trusted. - Jonathan M Davis
Jun 01 2013
On Saturday, 1 June 2013 at 21:02:44 UTC, Jonathan M Davis wrote:safe is for memory safety, meaning that safe code cannot corrupt memory. You can get segfaults due to null pointers and the like, but you can't have code which writes passed the end of a buffer, or which uses a freed memory, or does anything else which involves writing or reading from memory which variables aren't supposed to have access to.Not true. void foo(int* p) safe { *p = 0; } void main() { int[3] buf1 = [1, 2, 3]; int[1] buf2; int* p = buf2.ptr; --p; foo(p); import std.stdio; writeln(buf1); } For me, this prints [1, 2, 0]. You could easily come up with an example which writes to freed memory. You can argue that foo didn't "cause" this problem (the undefined behaviour from the pointer arithmetic in main did), but that's irrelevant: what guarantees do I have when I call a safe function that I don't have with any non- safe function? Do safe functions only provide guarantees when the inputs are valid, or is it the case the safe functions are guaranteed to not *introduce* any new undefined behaviour?
Jun 01 2013
On Saturday, June 01, 2013 23:34:09 Peter Alexander wrote:On Saturday, 1 June 2013 at 21:02:44 UTC, Jonathan M Davis wrote:They're guaranteed to not introduce any such behavior. They can't possibly make any guarantees if the caller did system operations and passed a bad pointer to the safe function. But if all of the functions in the call stack are safe, and you call an safe function, then you can't get any memory corruption unless it (or a function that it calls) calls an trusted function which was incorrectly verified by the programmer who marked it as trusted. - Jonathan M Davissafe is for memory safety, meaning that safe code cannot corrupt memory. You can get segfaults due to null pointers and the like, but you can't have code which writes passed the end of a buffer, or which uses a freed memory, or does anything else which involves writing or reading from memory which variables aren't supposed to have access to.Not true. void foo(int* p) safe { *p = 0; } void main() { int[3] buf1 = [1, 2, 3]; int[1] buf2; int* p = buf2.ptr; --p; foo(p); import std.stdio; writeln(buf1); } For me, this prints [1, 2, 0]. You could easily come up with an example which writes to freed memory. You can argue that foo didn't "cause" this problem (the undefined behaviour from the pointer arithmetic in main did), but that's irrelevant: what guarantees do I have when I call a safe function that I don't have with any non- safe function? Do safe functions only provide guarantees when the inputs are valid, or is it the case the safe functions are guaranteed to not *introduce* any new undefined behaviour?
Jun 01 2013
On Saturday, 1 June 2013 at 21:41:40 UTC, Jonathan M Davis wrote:They're guaranteed to not introduce any such behavior. They can't possibly make any guarantees if the caller did system operations and passed a bad pointer to the safe function. But if all of the functions in the call stack are safe, and you call an safe function, then you can't get any memory corruption unless it (or a function that it calls) calls an trusted function which was incorrectly verified by the programmer who marked it as trusted. - Jonathan M DavisUpdated example from above to show how safe can introduce UB. import std.stdio; class A { int[] data; ~this() { writeln(data); } } void foo(int[] a) safe { A x = new A; x.data = a; } void main() safe { int[4] y; foo(y); }
Jun 01 2013
On Saturday, 1 June 2013 at 21:46:01 UTC, Maxim Fomin wrote:Updated example from above to show how safe can introduce UB. void main() safe { int[4] y; foo(y); }I believe that's a compiler bug. safe requires: - No taking the address of a local variable or function parameter. A slice of a local static array should count as this, but it currently doesn't.
Jun 01 2013
On Saturday, June 01, 2013 23:45:59 Maxim Fomin wrote:On Saturday, 1 June 2013 at 21:41:40 UTC, Jonathan M Davis wrote:That's a known bug in safe. Slicing a static array should be considered system just like taking the address of a local variable is considered system: http://d.puremagic.com/issues/show_bug.cgi?id=8838 The guarantees of safe hold only so long as there are no holes in it, but any and all holes we find get fixed. Making ref be truly safe has been a large part of the recent ref discussions, as you can currently get away with doing something like ref int id(ref int i) { return i; } ref int foo() { int j; return id(j); } What it looks like we're going to do in this case is detect when this situation might happen and insert a runtime check which throws an Error if a reference to a local variable tries to escape, but regardless of the solution, it's an example of something that is currently considered safe by the compiler when it really isn't. All such holes need to be plugged, or safe isn't doing its job. So, if you find any more holes in safe, please report them in bugzilla: http://d.puremagic.com/issues - Jonathan M DavisThey're guaranteed to not introduce any such behavior. They can't possibly make any guarantees if the caller did system operations and passed a bad pointer to the safe function. But if all of the functions in the call stack are safe, and you call an safe function, then you can't get any memory corruption unless it (or a function that it calls) calls an trusted function which was incorrectly verified by the programmer who marked it as trusted. - Jonathan M DavisUpdated example from above to show how safe can introduce UB. import std.stdio; class A { int[] data; ~this() { writeln(data); } } void foo(int[] a) safe { A x = new A; x.data = a; } void main() safe { int[4] y; foo(y); }
Jun 01 2013
W dniu 01.06.2013 23:55, Jonathan M Davis pisze:The guarantees of safe hold only so long as there are no holes in it, but any and all holes we find get fixed. Making ref be truly safe has been a large part of the recent ref discussions, as you can currently get away with doing something like ref int id(ref int i) { return i; } ref int foo() { int j; return id(j); }I know that introducing another type qualifier may complicate things but this would be a compile time solution. I mean _scope_ type qualifier, so your example could be rewritten as: ref int id(ref int i) { return i; } ref int foo() { int j; return id(j); // error: could not pass scope(int) as ref int parameter } Taking an address of local variable would always yield a scope qualified type, scope(int) in this example. Obviously, scope qualified type could be passed to functions taking scope parameters: void bar(scope ref int i) { i = 10; } void foo() { int j = 0; bar(j); // ok assert(i == 10); } I think this could fill the safety holes.
Jun 01 2013
On Sunday, June 02, 2013 01:12:53 Piotr Szturmaj wrote:W dniu 01.06.2013 23:55, Jonathan M Davis pisze:Except that that makes it so that you can't return a ref argument, which is completely unacceptable. You need to be able to pass local variables to functions which accept ref in safe code, and you need functions which ref arguments to be able to return those arguments by ref. The only thing that we want to prevent is a local variable from escaping its original scope. It's perfectly valid that the id function accept a local variable by ref and returns it by ref. What's invalid is that the function that the local variable was declared in then returns it by ref. Manu suggested something similar to what you're suggesting with the addition of having making it so that you can then return variables as scope ref, in which case, the caller would see that the function was accepting by scope ref and returning by scope ref and that none of the variables that it accepted were scope ref in the caller. But this requires having having yet another annotation - scope ref - which Andrei was completely against (and Walter too IIRC), and it actually would end up making something like this illegal as well, went it shouldn't: scope ref int foo(scope ref int i, scope ref int j) { return j; } scope ref bar(scope ref int q) { int i; return foo(i, q); } The compiler can't know whether it's i or q that's being returned from foo, so it would have to given a compilation error, which is more restrictive than the runtime solution that has been proposed. So, you can do less, and you have to have mark up your functions with even more attributes, and it's yet another attribute for those learning the language to have to learn. Contrast this with simply inserting a very cheap runtime check in the rare cases where the compiler detects that a local variable might escape. No additional attributes are needed. So, the code is simpler, and there's less for people to learn. There's almost no performance hit (and if you want it to be zero, then use -noboundscheck). And we lose _zero_ functionality. None of that is the case with the scope ref proposal. Walter and Andrei do not like the idea of introducing even more attributes to solve this problem and were very excited to have this solution proposed (unfortunately, I'm not sure who proposed it though, since I missed that part of the conversation). And I'm inclined to agree with them. It's very simple and cheap. The only real downside is that it's caught at runtime rather than compile time, but it's quickly and easily caught at runtime, and the simplicity of it makes it seem like a great solution. - Jonathan M DavisThe guarantees of safe hold only so long as there are no holes in it, but any and all holes we find get fixed. Making ref be truly safe has been a large part of the recent ref discussions, as you can currently get away with doing something like ref int id(ref int i) { return i; } ref int foo() { int j; return id(j); }I know that introducing another type qualifier may complicate things but this would be a compile time solution. I mean _scope_ type qualifier, so your example could be rewritten as: ref int id(ref int i) { return i; } ref int foo() { int j; return id(j); // error: could not pass scope(int) as ref int parameter } Taking an address of local variable would always yield a scope qualified type, scope(int) in this example. Obviously, scope qualified type could be passed to functions taking scope parameters: void bar(scope ref int i) { i = 10; } void foo() { int j = 0; bar(j); // ok assert(i == 10); } I think this could fill the safety holes.
Jun 01 2013
Am 01.06.2013 23:34, schrieb Peter Alexander:On Saturday, 1 June 2013 at 21:02:44 UTC, Jonathan M Davis wrote:I always assumed that the role of safe is to behave like safe code in No C like tricks are allowed and in certain scenarios one could even disallow the linkage of modules not considered safe. For example in .NET, IIS only allows assemblies with unsafe code if configured by the administrator. Unsafe code is also forbidden for Go packages on App Engine. -- Paulosafe is for memory safety, meaning that safe code cannot corrupt memory. You can get segfaults due to null pointers and the like, but you can't have code which writes passed the end of a buffer, or which uses a freed memory, or does anything else which involves writing or reading from memory which variables aren't supposed to have access to.Not true. void foo(int* p) safe { *p = 0; } void main() { int[3] buf1 = [1, 2, 3]; int[1] buf2; int* p = buf2.ptr; --p; foo(p); import std.stdio; writeln(buf1); } For me, this prints [1, 2, 0]. You could easily come up with an example which writes to freed memory. You can argue that foo didn't "cause" this problem (the undefined behaviour from the pointer arithmetic in main did), but that's irrelevant: what guarantees do I have when I call a safe function that I don't have with any non- safe function? Do safe functions only provide guarantees when the inputs are valid, or is it the case the safe functions are guaranteed to not *introduce* any new undefined behaviour?
Jun 01 2013
On Saturday, 1 June 2013 at 21:02:44 UTC, Jonathan M Davis wrote:On Saturday, June 01, 2013 21:59:18 monarch_dodra wrote:OK. But by that standard, can't (mostly) anything be trusted? What about something that writes garbage, to a memory location it was *asked* to write to? Or if wrong usage of the function can lead to an inconsistence memory state, but without "out of bounds accesses"? For instance: "emplace!T(T* p)": This function takes the address of a T, and writes T.init over it. It does a memcopy, so it can't be safe, but I can 100% guarantee I'm not doing anything wrong, so I'm marking it as trusted. This should be fine, right? Or is raw memory copying alway unsafe? Now, technically, emplace can't be called in safe code, since it requires a pointer to begin with. But still, if I were to give emplace an "already constructed object", it will happily clobber that object for me, leaking the destructor, and possibly putting the program in an invalid memory state. Now, it was *my* fault for calling emplace with an already built object, but it was the ( trusted) emplace that clobbered-it. -------- Long story short, I'm having trouble drawing the line between system and trusted functions, especially in regards to these low level operations. The same question also works for, say "move" or "uninitializedArray": both 100% guarantee bounded memory access, but both can leave you with garbage in your memory...The way I understood it, safe defines a list of things that are or aren't legal inside the implementation of a function. It also changes the scheme of bounds checking, in release code. What bothers me though, is that from an interface point of view, it doesn't really mean anything (or at least, I haven't really understood anything). AFAIK: if I call something " safe", chances of a core dump are relatively "lower", but they can still happen: * A function that accepts a pointer as an argument can be marked safe, so all bets are off there, no, since the pointer can be dereferenced? * Member functions for structs that have pointers, too, can be marked safe... Or does it only mean "if you give me valid pointers, I can't core dump*"? (*ignoring current flaws, such as escaping slices from static arrays) The main reason about this question is that now I'm confused about trusted: what are the conditions a developer needs to take into account before marking a function " trusted" ? Ditto for member functions, when they operate on pointer members. Can those be safe? Yeah, overall, I'm confused as to what " safe" means from an interface point of view :(safe is for memory safety, meaning that safe code cannot corrupt memory. You can get segfaults due to null pointers and the like, but you can't have code which writes passed the end of a buffer, or which uses a freed memory, or does anything else which involves writing or reading from memory which variables aren't supposed to have access to. Assuming that there are no bugs in safe, the one thing that can invalidate it is trusted. With trusted code, it is the _programmer_ who is then guaranteeing that the code is actually safe. The code is doing something which is potentially not safe (and therefore is considered system by the compiler) but which _could_ be safe if the code is correct, and if the programmer is marking the code as trusted, they are then telling the compiler that they've verified that the code isn't doing anything which could corrupt memory. As long as the programmer doesn't screw that up, then any safe code calling that trusted function is indeed safe, but if the programmer screwed it up, then you could still get memory corruption. However, here's really no way to get around that problem with a systems language, since most code needs to eventually call something that's system (e.g. all I/O needs system stuff internally). But by limiting how much code is system or trusted, most code is safe with a minimal amount of code having to have been verified by an appropriately competent programmer as being trusted. - Jonathan M Davis
Jun 01 2013
On Saturday, June 01, 2013 23:41:32 monarch_dodra wrote:On Saturday, 1 June 2013 at 21:02:44 UTC, Jonathan M Davis wrote:When a programmer marks a function as trusted, they are saying that they guarantee that the function will not do anything to corrupt memory. So, yes, a programmer could mark absolutely anything as trusted - including stuff that is blatantly unsafe and will do all kinds of nasty stuff - but that's the programmer's fault. The compiler told them that it was system and therefore could not be verified to be memory safe, and the programmer insisted that it was memory safe. Any code which cannot be guaranteed to be actually safe should not be marked with trusted. It's up to the programmer figure out whether what they're doing is valid or not.On Saturday, June 01, 2013 21:59:18 monarch_dodra wrote:OK. But by that standard, can't (mostly) anything be trusted? What about something that writes garbage, to a memory location it was *asked* to write to? Or if wrong usage of the function can lead to an inconsistence memory state, but without "out of bounds accesses"?The way I understood it, safe defines a list of things that are or aren't legal inside the implementation of a function. It also changes the scheme of bounds checking, in release code. What bothers me though, is that from an interface point of view, it doesn't really mean anything (or at least, I haven't really understood anything). AFAIK: if I call something " safe", chances of a core dump are relatively "lower", but they can still happen: * A function that accepts a pointer as an argument can be marked safe, so all bets are off there, no, since the pointer can be dereferenced? * Member functions for structs that have pointers, too, can be marked safe... Or does it only mean "if you give me valid pointers, I can't core dump*"? (*ignoring current flaws, such as escaping slices from static arrays) The main reason about this question is that now I'm confused about trusted: what are the conditions a developer needs to take into account before marking a function " trusted" ? Ditto for member functions, when they operate on pointer members. Can those be safe? Yeah, overall, I'm confused as to what " safe" means from an interface point of view :(safe is for memory safety, meaning that safe code cannot corrupt memory. You can get segfaults due to null pointers and the like, but you can't have code which writes passed the end of a buffer, or which uses a freed memory, or does anything else which involves writing or reading from memory which variables aren't supposed to have access to. Assuming that there are no bugs in safe, the one thing that can invalidate it is trusted. With trusted code, it is the _programmer_ who is then guaranteeing that the code is actually safe. The code is doing something which is potentially not safe (and therefore is considered system by the compiler) but which _could_ be safe if the code is correct, and if the programmer is marking the code as trusted, they are then telling the compiler that they've verified that the code isn't doing anything which could corrupt memory. As long as the programmer doesn't screw that up, then any safe code calling that trusted function is indeed safe, but if the programmer screwed it up, then you could still get memory corruption. However, here's really no way to get around that problem with a systems language, since most code needs to eventually call something that's system (e.g. all I/O needs system stuff internally). But by limiting how much code is system or trusted, most code is safe with a minimal amount of code having to have been verified by an appropriately competent programmer as being trusted. - Jonathan M DavisFor instance: "emplace!T(T* p)": This function takes the address of a T, and writes T.init over it. It does a memcopy, so it can't be safe, but I can 100% guarantee I'm not doing anything wrong, so I'm marking it as trusted. This should be fine, right? Or is raw memory copying alway unsafe?Raw memory copying should be fine as long as the memory being copied from and the memory being copied to is valid.Now, technically, emplace can't be called in safe code, since it requires a pointer to begin with.Pointers can be in safe code. They're perfectly safe in and of themselves. It's certain operations on pointers which are unsafe (such as pointer arithmetic).But still, if I were to give emplace an "already constructed object", it will happily clobber that object for me, leaking the destructor, and possibly putting the program in an invalid memory state. Now, it was *my* fault for calling emplace with an already built object, but it was the ( trusted) emplace that clobbered-it.Well, given that the safety of the operation relies on what's being passed in, the operation itself can't reasonably be marked as safe, because you can't guarantee that the operation isn't going to corrupt memory. - Jonathan M Davis
Jun 01 2013
On Saturday, 1 June 2013 at 22:15:00 UTC, Jonathan M Davis wrote:On Saturday, June 01, 2013 23:41:32 monarch_dodra wrote:Well, by "mostly", I did mean stuff that's not blatantly wrong. I don't usually write stuff with the express objective of clobbering memory. But given the previous answers, I think I see why anything that should work can't be marked safe.OK. But by that standard, can't (mostly) anything be trusted? What about something that writes garbage, to a memory location it was *asked* to write to? Or if wrong usage of the function can lead to an inconsistence memory state, but without "out of bounds accesses"?When a programmer marks a function as trusted, they are saying that they guarantee that the function will not do anything to corrupt memory. So, yes, a programmer could mark absolutely anything as trusted - including stuff that is blatantly unsafe and will do all kinds of nasty stuff - but that's the programmer's fault.But isn't that exactly the same as my "void foo(int* p) safe{*p = 0}" example ? That relies on what is being passed in to guarantee safety :/ confusedBut still, if I were to give emplace an "already constructed object", it will happily clobber that object for me, leaking the destructor, and possibly putting the program in an invalid memory state. Now, it was *my* fault for calling emplace with an already built object, but it was the ( trusted) emplace that clobbered-it.Well, given that the safety of the operation relies on what's being passed in, the operation itself can't reasonably be marked as safe, because you can't guarantee that the operation isn't going to corrupt memory.
Jun 02 2013
On Sunday, June 02, 2013 09:59:08 monarch_dodra wrote:On Saturday, 1 June 2013 at 22:15:00 UTC, Jonathan M Davis wrote:I don't know. The function isn't doing anything unsafe in and of itself. foo _can_ be marked as safe, because none of the operations that it's doing are unsafe. The problem is when the caller does something unsafe. So, maybe that's the answer. It's the caller which can't be marked as safe, because it's doing something stupid. foo isn't doing anything unsafe, so it _can_ be safe. I think that part of the problem is the fact that safety is generally viewed with the idea that unsafe stuff is called by safe stuff and not that unsafe stuff is called by safe stuff. And these cases under discussion are cases where the safe stuff ends up corrupting stuff if called from unsafe stuff. So, maybe emplace should be marked as trusted (assuming that it can't corrupt anything if called from an safe function and that its safety does not depend on its arguments). It _is_ a bit of a tough topic though. - Jonathan M DavisOn Saturday, June 01, 2013 23:41:32 monarch_dodra wrote:But isn't that exactly the same as my "void foo(int* p) safe{*p = 0}" example ? That relies on what is being passed in to guarantee safety :/ confusedNow, it was *my* fault for calling emplace with an already built object, but it was the ( trusted) emplace that clobbered-it.Well, given that the safety of the operation relies on what's being passed in, the operation itself can't reasonably be marked as safe, because you can't guarantee that the operation isn't going to corrupt memory.
Jun 02 2013
On Sun, 02 Jun 2013 03:59:08 -0400, monarch_dodra <monarchdodra gmail.com> wrote:On Saturday, 1 June 2013 at 22:15:00 UTC, Jonathan M Davis wrote:provable safe depends on the precondition that its parameters are valid and safe. The easiest way to do this is to mark main as safe. Then you can't go unsafe. As people have pointed out, there are bugs/holes. They need to be fixed. trusted should be used VERY cautiously. It basically says "I know this is safe, but the compiler can't prove it". These situations should be very very rare. Think of safe functions as bricks. By themselves, they are solid and will hold up a building well. But if you put them on top of garbage, they will be as useless as cardboard. -SteveWell, given that the safety of the operation relies on what's being passed in, the operation itself can't reasonably be marked as safe, because you can't guarantee that the operation isn't going to corrupt memory.But isn't that exactly the same as my "void foo(int* p) safe{*p = 0}" example ? That relies on what is being passed in to guarantee safety :/ confused
Jun 03 2013
On Monday, June 03, 2013 12:03:51 Steven Schveighoffer wrote:Think of safe functions as bricks. By themselves, they are solid and will hold up a building well. But if you put them on top of garbage, they will be as useless as cardboard.Nice analogy. - Jonathan M Davis
Jun 03 2013
On 6/1/13 3:59 PM, monarch_dodra wrote:Yeah, overall, I'm confused as to what " safe" means from an interface point of view :(If you call the function from a program with memory integrity and it returns, it hasn't compromised the memory integrity of that program. Homework: define memory integrity :o). Andrei
Jun 01 2013
On Saturday, 1 June 2013 at 21:45:18 UTC, Andrei Alexandrescu wrote:On 6/1/13 3:59 PM, monarch_dodra wrote:OK. In a word, I guess that makes sense. I'll stick to that standard. But there is still the "emplace" question: When I call "emplace" on a pointer to a built object, is does the program still have memory integrity? At the end of the call, was it emplace that compromised it? Was it the exact instance the "call was initialized"?Yeah, overall, I'm confused as to what " safe" means from an interface point of view :(If you call the function from a program with memory integrity and it returns, it hasn't compromised the memory integrity of that program. Homework: define memory integrity :o). Andrei
Jun 01 2013
On Sunday, June 02, 2013 00:04:18 monarch_dodra wrote:On Saturday, 1 June 2013 at 21:45:18 UTC, Andrei Alexandrescu wrote:Because the safety of the function depends on the caller, I'd say that it should be system. - Jonathan M DavisOn 6/1/13 3:59 PM, monarch_dodra wrote:OK. In a word, I guess that makes sense. I'll stick to that standard. But there is still the "emplace" question: When I call "emplace" on a pointer to a built object, is does the program still have memory integrity? At the end of the call, was it emplace that compromised it? Was it the exact instance the "call was initialized"?Yeah, overall, I'm confused as to what " safe" means from an interface point of view :(If you call the function from a program with memory integrity and it returns, it hasn't compromised the memory integrity of that program. Homework: define memory integrity :o). Andrei
Jun 01 2013