CSC447
Concepts of Programming Languages
Safety
Instructor: James Riely
C type
-
What do you make of this program?
$ clang -m32 typing-00.c && ./a.out
(float)*p = 2123456768.000000
*(float*)p = 96621069057346178268049192388430659584.000000
i*s = -1047484
| int main() { |
| int *p = (int*) malloc (sizeof(int)); |
| *p = 2123456789; |
| |
| printf ("(float)*p = %f\n", (float)*p); |
| printf ("*(float*)p = %f\n", *(float*)p); |
| |
| int i = 2; |
| char s[] = "three"; |
| |
| printf ("i*s = %ld\n", i*(long)s); |
| } |
Unsafe access
-
Memory location contains data written at a given type (such as character array)
-
The same memory location is read without permission or interpreted at an incompatible type (such as int)
-
This is an unsafe access
-
Lox prevents unsafe access by throwing an exception
fun f(x) { return x - 42; }
fun main() { f("dog"); }
lox11> main();
Operands must be numbers.
Array bounds
$ clang -m32 typing-03.c && ./a.out
f=10.000000, a[0]=10 i=10
f=10.000000, a[0]=10 i=32401
f=96621069057346178268049192388430659584.000000, a[0]=10 i=32401
| int main () { |
| float f = 10; |
| int a[] = { 10 }; |
| short i = 10; |
| printf ("f=%f, a[0]=%d i=%d\n", f, a[0], i); |
| a[-1] = 2123456789; printf ("f=%f, a[0]=%d i=%d\n", f, a[0], i); |
| a[1] = 2123456789; printf ("f=%f, a[0]=%d i=%d\n", f, a[0], i); |
| } |
Aliasing a pointer on the Stack
$ gcc typing-06.c && ./a.out
x=2123456789, y=2123456789.000000
x=2123456789, z=38685644468023060038942720.000000
| int main() { |
| int x = 2123456789; |
| double y = x; |
| printf ("x=%d, y=%f\n", x, y); |
| double *p = &x; |
| double z = *p; |
| printf ("x=%d, z=%f\n", x, z); |
| } |
Aliasing a pointer on the Heap
$ gcc typing-07.c && ./a.out
*fp=10.000000, *ip=1092616192
fp=0x1063010, ip=0x1063010
| int main() { |
| int* ip = (int*) malloc (sizeof(int)); |
| *ip = 10; |
| free(ip); |
| float* fp = (float*) malloc (sizeof(float)); |
| *fp = 10; |
| printf ("*fp=%f, *ip=%d\n", *fp, *ip); |
| printf (" fp=%p, ip=%p\n", fp, ip); |
| } |
Function pointers
$ clang -m32 typing-04.c && ./a.out
hurray!
ouch!
| void unsafeCommand () { printf ("ouch!\n"); } |
| void safeCommand () { printf ("hurray!\n"); } |
| int main () { |
| int diff = &unsafeCommand - &safeCommand; |
| void (*c) () = &safeCommand; |
| c(); |
| c += diff; |
| c(); |
| } |
Function pointers with arguments
$ clang -m32 typing-05.c && ./a.out
i=2123456789
f=96621069057346178268049192388430659584.000000
| void floatCommand (float f) { printf ("f=%f\n", f); } |
| void intCommand (int i) { printf ("i=%d\n", i); } |
| int main () { |
| int diff = (void*)&intCommand - (void*)&floatCommand; |
| void (*c) (int) = &intCommand; |
| int j = 2123456789; |
| c(j); |
| c -= diff; |
| c(j); |
| } |
Java
To be safe, Java does the following
-
Disallows pointers into the stack
-
Disallows pointer arithmetic
-
Disallows explicit
free
-
Checks array bounds
-
Checks potentially unsafe casts
Bounds checking
$ javac Typing04.java && java Typing04
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: -1
| class Typing04 { |
| public static void main (String[] args) { |
| Object[] bs = new Object[4]; |
| Object b = bs[-1]; |
| } |
| } |
Checked casts
$ javac Typing05.java && java Typing05
Exception in thread "main" java.lang.ClassCastException: C cannot be cast to B
| class A { int x; } |
| class B extends A { float y; } |
| class C extends A { char c; } |
| |
| class Typing05 { |
| static void f (B b) { |
| A a = b; |
| } |
| static void g (A a) { |
| B b = (B) a; |
| } |
| public static void main (String[] args) { |
| f (new B()); |
| g (new C()); |
| } |
| } |
Compare with dynamic cast in C++
Checked array assignment
$ javac Typing03.java && java Typing03
Exception in thread "main" java.lang.ArrayStoreException: C
| class A { int x=1; } |
| class B extends A { float y=2; } |
| class C extends A { char z='3'; } |
| |
| class Typing03 { |
| public static void main (String[] args) { |
| B[] bs = new B[1]; |
| A[] as = bs; |
| as[0] = new C(); |
| B b = bs[0]; |
| System.out.println (b.y); |
| } |
| } |
This is a design flaw -- More later
Safety
-
Traditional systems languages are purposefully unsafe
-
Assembly, C, C++, Objective-C, C#-unmanaged, ...
-
Recent application languages are meant to be safe
-
Lox, Java, Scheme, Javascript, Python, , C#-managed, ...
-
Recent systems languages attempt to isolate the unsafe bits
Safety and Dynamism
-
Even in safe languages, the overuse of dynamic checks allows program flaws to make it into production
-
Overuse of
null
is considered a billion dollar mistake
-
Which is better?
-
Security hole due to lack of null checks
-
Program crashes on null pointer reference
-
No good options?