Search
Hello Michel,

> I'm not sure this works so well. Look at this:
>
> module memory;   // unsafe interface - unsafe impl.
> extern (C) void* malloc(int);
> extern (C) void free(void*);
> module (system) my.system;   // safe interface - unsafe impl.
> import memory;
> void test() { auto i = malloc(10); free(i); }   // ok: unsafe impl.
> allowed
> module (safe) my.safe;   // safe interface - safe impl.
> import memory;
> void test() { auto i = malloc(10); free(i); }   // error: malloc,
> free
> are unsafe
> How is this supposed to work correctly with and without the "-safe"
> compiler flag? The way you define things "-safe" would make module
> memory safe for use while it is not.

I'm saying the module memory would not compile when compiler is called with -safe switch.

the compiler would try to compile each module without safety specification, as if they were *marked* (safe) - which will not succeed for module memory in this case.

In this setting, the reasons to have -safe compiler switch are not so important, they are more like convenience, meaning more like -forcesafe.

You would want to use this flag only when you *need* to make sure your application is safe, usually when you are using other libraries. By this switch you can prevent compilation of unsafe application in case some other library silently changes safe module to unsafe in newer version.


Michal Minich wrote:
> Hello Michel,
>
>> I'm not sure this works so well. Look at this:
>>
>> module memory;   // unsafe interface - unsafe impl.
>> extern (C) void* malloc(int);
>> extern (C) void free(void*);
>> module (system) my.system;   // safe interface - unsafe impl.
>> import memory;
>> void test() { auto i = malloc(10); free(i); }   // ok: unsafe impl.
>> allowed
>> module (safe) my.safe;   // safe interface - safe impl.
>> import memory;
>> void test() { auto i = malloc(10); free(i); }   // error: malloc,
>> free
>> are unsafe
>> How is this supposed to work correctly with and without the "-safe"
>> compiler flag? The way you define things "-safe" would make module
>> memory safe for use while it is not.
>
> I'm saying the module memory would not compile when compiler is called with -safe switch.
>
> the compiler would try to compile each module without safety specification, as if they were *marked* (safe) - which will not succeed for module memory in this case.
>
> In this setting, the reasons to have -safe compiler switch are not so important, they are more like convenience, meaning more like -forcesafe.
> You would want to use this flag only when you *need* to make sure your application is safe, usually when you are using other libraries. By this switch you can prevent compilation of unsafe application in case some other library silently changes safe module to unsafe in newer version.
>
>
Doesn't work. There are system modules which CANNOT safely be called from safe modules -- eg extern(C) functions. They MUST have unsafe interfaces.


Walter Bright wrote:
> Andrei Alexandrescu wrote:
>> Sketch of the safe rules:
>>
>> \begin{itemize*}
>> \item No @cast@ from a pointer type to an integral type and vice versa
>
> replace integral type with non-pointer type.
>
>> \item No @cast@ between unrelated pointer types
>> \item Bounds checks on all array accesses
>> \item  No  unions  that  include  a reference  type  (array,  @class@,
>>   pointer, or @struct@ including such a type)
>
> pointers are not a reference type. Replace "reference type" with "pointers or reference types".
>
>> \item No pointer arithmetic
>
>> \item No escape of a pointer  or reference to a local variable outside
>>   its scope
>
> revise: cannot take the address of a local or a reference.
>
>> \item Cross-module function calls must only go to other @safe@ modules
>> \end{itemize*}
>
> . no inline assembler
> . no casting away of const, immutable, or shared

Ok, here's what I have now:

\begin{itemize*}
\item No @cast@ from a pointer type to a non-pointer type (e.g.~@int@)
and vice versa
\item No @cast@ between unrelated pointer types
\item Bounds checks on all array accesses
\item No  unions that include  pointer type, a reference  type (array,
@class@), or a @struct@ including such a type
\item No pointer arithmetic
\item Taking the  address of a local is forbidden  (in fact the needed
restriction is to  not allow such an address to  escape, but that is
more difficult to track)
\item Cross-module function calls must only go to other @safe@ modules
\item No inline assembler
\item No casting away of @const@, @immutable@, or @shared@
\end{itemize*}

Andrei

Don wrote:
> Andrei Alexandrescu wrote:
>> Don wrote:
>>> Andrei Alexandrescu wrote:
>
>> module(safe) is not a comment. We need three types of modules because of the interaction between what the module declares and what the command line wants.
>>
>> Let's assume the default, no-flag build allows unsafe code, like right now. Then, module(safe) means that the module volunteers itself for tighter checking, and module(system) is same as module unadorned.
>>
>> But then if the user compiles with -safe, module(safe) is the same as module unadorned, and module(system) allows for unchecked operations in that particular module. I was uncomfortable with this, but Walter convinced me that D's charter is not to allow sandbox compilation and execution of malicious code. If you have the sources, you may as well take a look at their module declarations if you have some worry.
>>
>> Regardless on the result of the debate regarding the default compilation mode, if the change of that default mode is allowed in the command line, then we need both module(safe) and module(system).
>
> When would it be MANDATORY for a module to be compiled in safe mode?

module(safe) entails safe mode, come hell or high water.

> If module(safe) implies bound-checking *cannot* be turned off for that module, would any standard library modules be module(safe)?

I think most or all of the standard library is trusted. But don't forget that std is a bad example of a typical library or program because std interfaces programs with the OS.

>>> This actually seems pretty similar to public/private.
>>> I see three types of modules:
>>>
>>> module  : the default, should compile in -safe mode.
>>> module(system) : Modules which need to do nasty stuff inside, but for which all the public functions are safe.
>>> module(sysinternal/restricted/...): Modules which exist only to support system modules. This will include most APIs to C libraries.
>>>
>>> Modules in the outer ring need to be prevented from calling ones in the inner ring.
>>
>> Well I wouldn't want to go any dirtier than "system", so my "system" would be your "sysinternal". I'd like to milden "system" a bit like in e.g. "trusted", which would be your "system".
>
> Yeah, the names don't matter. The thing is, modules in the inner ring are extremely rare. I'd hope there'd be just a few in druntime, and no public ones at all in Phobos.

That sounds plausible.

Andrei

On Tue, 03 Nov 2009 23:13:14 -0600, Andrei Alexandrescu wrote:

>> I think the only real option is to have the importer decide if it is trusted.
>
> That can't work. I can't say that stdc.stdlib is trusted no matter how hard I try. I mean free is there!

I would like to disagree here.

void free(void *ptr);

free() takes a pointer. There is no way for the coder to get a pointer in SafeD, compiler won't let them, so the function is unusable by a "safe" module even if the function is imported.

Jesse Phillips wrote:
> On Tue, 03 Nov 2009 23:13:14 -0600, Andrei Alexandrescu wrote:
>
>>> I think the only real option is to have the importer decide if it is
>>> trusted.
>> That can't work. I can't say that stdc.stdlib is trusted no matter how
>> hard I try. I mean free is there!
>
> I would like to disagree here.
>
> void free(void *ptr);
>
> free() takes a pointer. There is no way for the coder to get a pointer in SafeD, compiler won't let them, so the function is unusable by a "safe" module even if the function is imported.

Pointers should be available to SafeD, just not certain operations with them.

Andrei

Hello Don,

> Michal Minich wrote:
>
>> Hello Michel,
>>
>>> I'm not sure this works so well. Look at this:
>>>
>>> module memory;   // unsafe interface - unsafe impl.
>>> extern (C) void* malloc(int);
>>> extern (C) void free(void*);
>>> module (system) my.system;   // safe interface - unsafe impl.
>>> import memory;
>>> void test() { auto i = malloc(10); free(i); }   // ok: unsafe impl.
>>> allowed
>>> module (safe) my.safe;   // safe interface - safe impl.
>>> import memory;
>>> void test() { auto i = malloc(10); free(i); }   // error: malloc,
>>> free
>>> are unsafe
>>> How is this supposed to work correctly with and without the "-safe"
>>> compiler flag? The way you define things "-safe" would make module
>>> memory safe for use while it is not.
>> I'm saying the module memory would not compile when compiler is
>> called with -safe switch.
>>
>> the compiler would try to compile each module without safety
>> specification, as if they were *marked* (safe) - which will not
>> succeed for module memory in this case.
>>
>> In this setting, the reasons to have -safe compiler switch are not so
>> important, they are more like convenience, meaning more like
>> -forcesafe. You would want to use this flag only when you *need* to
>> make sure your application is safe, usually when you are using other
>> libraries. By this switch you can prevent compilation of unsafe
>> application in case some other library silently changes safe module
>> to unsafe in newer version.
>>
> Doesn't work. There are system modules which CANNOT safely be called
> from safe modules -- eg extern(C) functions. They MUST have unsafe
> interfaces.
>

Hello Don,

> Michal Minich wrote:
>
>> Hello Michel,
>>
>>> I'm not sure this works so well. Look at this:
>>>
>>> module memory;   // unsafe interface - unsafe impl.
>>> extern (C) void* malloc(int);
>>> extern (C) void free(void*);
>>> module (system) my.system;   // safe interface - unsafe impl.
>>> import memory;
>>> void test() { auto i = malloc(10); free(i); }   // ok: unsafe impl.
>>> allowed
>>> module (safe) my.safe;   // safe interface - safe impl.
>>> import memory;
>>> void test() { auto i = malloc(10); free(i); }   // error: malloc,
>>> free
>>> are unsafe
>>> How is this supposed to work correctly with and without the "-safe"
>>> compiler flag? The way you define things "-safe" would make module
>>> memory safe for use while it is not.
>> I'm saying the module memory would not compile when compiler is
>> called with -safe switch.
>>
>> the compiler would try to compile each module without safety
>> specification, as if they were *marked* (safe) - which will not
>> succeed for module memory in this case.
>>
>> In this setting, the reasons to have -safe compiler switch are not so
>> important, they are more like convenience, meaning more like
>> -forcesafe. You would want to use this flag only when you *need* to
>> make sure your application is safe, usually when you are using other
>> libraries. By this switch you can prevent compilation of unsafe
>> application in case some other library silently changes safe module
>> to unsafe in newer version.
>>
> Doesn't work. There are system modules which CANNOT safely be called
> from safe modules -- eg extern(C) functions. They MUST have unsafe
> interfaces.
>

then they are not (system) modules. they are just modules with no specification.

When not using -safe switch, you cannot call from (safe) to module with no safety specification (you can only call (safe) and (system))

When using -safe switch, there does not exists module with not safety specification, all plain modules will be marked (safe), and (system) modules are unchanged. You will not be able to call extern(C) functions from (safe) module, because module in which they are defined will be marked (safe), and will not compile itself. There is the problem I think you are referring to: (system) modules should not be affected by -safe flag. User of module believes (system) is safe, so the (system) module can call anything anytime. So I would suggest such update:

when -safe switch is not used:
module name;            // interface: unsafe impl: unsafe module (system) name;   // interface: safe   impl: unsafe module (safe) name;     // interface: safe   impl: safe

when -safe switch is used:
module name;            // interface: unsafe impl: unsafe   -- when imported from system module
module name;            // interface: safe   impl: safe     -- when imported from safe modules
module (system) name;   // interface: safe   impl: unsafe module (safe) name;     // interface: safe   impl: safe

this means, that when -safe switch is used, that modules with no specification would be marked (safe) only when imported by modules marked as (safe). When they are imported from (system) modules, they will not be marked (safe). There is no need to another check if both (safe) and (system) nodules imports given module, because import from (safe) modules is stronger check, which is always fulfils by import from (system module).

In other words, (system) module does not need to perform any more checking when -safe flag is used, it is same as if it not used.


Andrei Alexandrescu, el  3 de noviembre a las 17:54 me escribiste:
> Leandro Lucarella wrote:
> >Andrei Alexandrescu, el  3 de noviembre a las 16:33 me escribiste:
> >>SafeD is, unfortunately, not finished at the moment. I want to leave in place a stub that won't lock our options. Here's what we currently have:
> >>
> >>module(system) calvin;
> >>
> >>This means calvin can do unsafe things.
> >>
> >>module(safe) susie;
> >>
> >>This means susie commits to extra checks and therefore only a subset of D.
> >>
> >>module hobbes;
> >>
> >>This means hobbes abides to whatever the default safety setting is.
> >>
> >>The default safety setting is up to the compiler. In dmd by default it is "system", and can be overridden with "-safe".
> >
> >What's the rationale for letting the compiler decide? I can't see nothing but trouble about this. A module will tipically be writen to be safe or system, I think the default should be defined (I'm not sure what the default should be though).
>
> The parenthesis pretty much destroys your point :o).

I guess this is a joke, but I have to ask: why? I'm not sure about plenty of stuff, that doesn't mean they are pointless.

> I don't think letting the implementation decide is a faulty model. If you know what you want, you say it. Otherwise it means you don't care.

I can't understand how you can't care. Maybe I'm misunderstanding the proposal, since nobody else seems to see a problem here.

--
Leandro Lucarella (AKA luca)                     http://llucax.com.ar/
----------------------------------------------------------------------
GPG Key: 5F5A8D05 (F8CD F9A7 BF00 5431 4145  104C 949E BFB6 5F5A 8D05)
----------------------------------------------------------------------
CAMPAÑA POR LA PAZ: APLASTARON JUGUETES BÉLICOS
-- Crónica TV

Walter Bright, el  3 de noviembre a las 16:21 me escribiste:
> Andrei Alexandrescu wrote:
> >Sketch of the safe rules:
> >
> >\begin{itemize*}
> >\item No @cast@ from a pointer type to an integral type and vice versa
>
> replace integral type with non-pointer type.
>
> >\item No @cast@ between unrelated pointer types
> >\item Bounds checks on all array accesses
> >\item  No  unions  that  include  a reference  type  (array,  @class@,
> >  pointer, or @struct@ including such a type)
>
> pointers are not a reference type. Replace "reference type" with "pointers or reference types".

Strictly speaking, arrays are not reference types either, right?

--
Leandro Lucarella (AKA luca)                     http://llucax.com.ar/
----------------------------------------------------------------------
GPG Key: 5F5A8D05 (F8CD F9A7 BF00 5431 4145  104C 949E BFB6 5F5A 8D05)
----------------------------------------------------------------------
Nos retiramos hasta la semana que viene reflexionando sobre nuestras
vidas: "Qué vida de mier'... Qué vida de mier'!"
-- Sidharta Kiwi

Michel Fortin Wrote:

> How is this supposed to work correctly with and without the "-safe" compiler flag? The way you define things "-safe" would make module memory safe for use while it is not.

"-safe" would cause the compiler to check if the code was safe and error out if it wasn't. Not sure how it would work out for the precompiled libraries.