about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorKartik K. Agaram <vc@akkartik.com>2016-08-21 08:08:41 -0700
committerKartik K. Agaram <vc@akkartik.com>2016-08-21 08:12:13 -0700
commitff16e04f57347e5a327099a61f8b16d5ba50abf3 (patch)
tree1e1bc7ed285652e10e6f9227ea0cd4bf242866fb
parente440e3e06efddb2cb6a821df32a3e07cd748bf8a (diff)
downloadmu-ff16e04f57347e5a327099a61f8b16d5ba50abf3.tar.gz
3237
More checks for unsafe filesystem primitives. Most important, make sure
the product of any $close-file instruction is never ignored, and that
it's the same variable as the ingredient. (No way to indicate that in Mu
code yet, but then Mu code should always be safe and not require such
checks.)
-rw-r--r--087file.cc36
-rw-r--r--088file.mu4
2 files changed, 38 insertions, 2 deletions
diff --git a/087file.cc b/087file.cc
index 6db0008d..b9a16e94 100644
--- a/087file.cc
+++ b/087file.cc
@@ -23,6 +23,14 @@ case _OPEN_FILE_FOR_READING: {
     raise << maybe(get(Recipe, r).name) << "first ingredient of '$open-file-for-reading' should be a string, but got '" << to_string(inst.ingredients.at(0)) << "'\n" << end();
     break;
   }
+  if (SIZE(inst.products) != 1) {
+    raise << maybe(get(Recipe, r).name) << "'$open-file-for-reading' requires exactly one product, but got '" << inst.original_string << "'\n" << end();
+    break;
+  }
+  if (!is_mu_number(inst.products.at(0))) {
+    raise << maybe(get(Recipe, r).name) << "first product of '$open-file-for-reading' should be a number (file handle), but got '" << to_string(inst.products.at(0)) << "'\n" << end();
+    break;
+  }
   break;
 }
 :(before "End Primitive Recipe Implementations")
@@ -50,6 +58,14 @@ case _OPEN_FILE_FOR_WRITING: {
     raise << maybe(get(Recipe, r).name) << "first ingredient of '$open-file-for-writing' should be a string, but got '" << to_string(inst.ingredients.at(0)) << "'\n" << end();
     break;
   }
+  if (SIZE(inst.products) != 1) {
+    raise << maybe(get(Recipe, r).name) << "'$open-file-for-writing' requires exactly one product, but got '" << inst.original_string << "'\n" << end();
+    break;
+  }
+  if (!is_mu_number(inst.products.at(0))) {
+    raise << maybe(get(Recipe, r).name) << "first product of '$open-file-for-writing' should be a number (file handle), but got '" << to_string(inst.products.at(0)) << "'\n" << end();
+    break;
+  }
   break;
 }
 :(before "End Primitive Recipe Implementations")
@@ -76,6 +92,14 @@ case _READ_FROM_FILE: {
     raise << maybe(get(Recipe, r).name) << "first ingredient of '$read-from-file' should be a number, but got '" << to_string(inst.ingredients.at(0)) << "'\n" << end();
     break;
   }
+  if (SIZE(inst.products) != 1) {
+    raise << maybe(get(Recipe, r).name) << "'$read-from-file' requires exactly one product, but got '" << inst.original_string << "'\n" << end();
+    break;
+  }
+  if (!is_mu_character(inst.products.at(0))) {
+    raise << maybe(get(Recipe, r).name) << "first product of '$read-from-file' should be a character, but got '" << to_string(inst.products.at(0)) << "'\n" << end();
+    break;
+  }
   break;
 }
 :(before "End Primitive Recipe Implementations")
@@ -125,6 +149,10 @@ case _WRITE_TO_FILE: {
     raise << maybe(get(Recipe, r).name) << "second ingredient of '$write-to-file' should be a character, but got '" << to_string(inst.ingredients.at(0)) << "'\n" << end();
     break;
   }
+  if (!inst.products.empty()) {
+    raise << maybe(get(Recipe, r).name) << "'$write-to-file' writes to no products, but got '" << inst.original_string << "'\n" << end();
+    break;
+  }
   break;
 }
 :(before "End Primitive Recipe Implementations")
@@ -165,6 +193,14 @@ case _CLOSE_FILE: {
     raise << maybe(get(Recipe, r).name) << "first ingredient of '$close-file' should be a number, but got '" << to_string(inst.ingredients.at(0)) << "'\n" << end();
     break;
   }
+  if (SIZE(inst.products) != 1) {
+    raise << maybe(get(Recipe, r).name) << "'$close-file' requires exactly one product, but got '" << inst.original_string << "'\n" << end();
+    break;
+  }
+  if (inst.products.at(0).name != inst.ingredients.at(0).name) {
+    raise << maybe(get(Recipe, r).name) << "'$close-file' requires its product to be the same as its ingredient, but got '" << inst.original_string << "'\n" << end();
+    break;
+  }
   break;
 }
 :(before "End Primitive Recipe Implementations")
diff --git a/088file.mu b/088file.mu
index 2eeeec2c..b18919d0 100644
--- a/088file.mu
+++ b/088file.mu
@@ -51,7 +51,7 @@ def transmit-from-file file:number, sink:address:sink:character -> sink:address:
     loop
   }
   sink <- close sink
-  $close-file file
+  file <- $close-file file
 ]
 
 def transmit-from-text contents:address:array:character, sink:address:sink:character -> sink:address:sink:character [
@@ -87,5 +87,5 @@ def transmit-to-file file:number, source:address:source:character -> file:number
     $write-to-file file, c
     loop
   }
-  $close-file file
+  file <- $close-file file
 ]