1
0
mirror of https://github.com/janet-lang/janet synced 2024-06-16 10:19:55 +00:00

Add more sandbox capabilities.

Add more granularity to ffi sandbox capabilities - distinguish between
using FFI functions, creating FFI functions, and creating executable
memory.
This commit is contained in:
Calvin Rose 2023-06-04 18:48:34 -05:00
parent 6509e37c84
commit 528a516390
4 changed files with 33 additions and 29 deletions

View File

@ -6,7 +6,7 @@
<img src="https://raw.githubusercontent.com/janet-lang/janet/master/assets/janet-w200.png" alt="Janet logo" width=200 align="left"> <img src="https://raw.githubusercontent.com/janet-lang/janet/master/assets/janet-w200.png" alt="Janet logo" width=200 align="left">
**Janet** is a dynamic language and bytecode interpreter for system scripting, expressive automation, and **Janet** is a programming language for system scripting, expressive automation, and
extending programs written in C or C++ with user scripting capabilities. extending programs written in C or C++ with user scripting capabilities.
There is a REPL for trying out the language, as well as the ability There is a REPL for trying out the language, as well as the ability
@ -105,34 +105,31 @@ See the examples directory for all provided example programs.
## Use Cases ## Use Cases
Janet makes a good system scripting language, or a language to embed in other programs. Janet makes a good system scripting language, or a language to embed in other programs.
It's like Lua and Guile in that regard. It has more built-in functionality and a richer core language than It's like Lua and GNU Guile in that regard. It has more built-in functionality and a richer core language than
Lua, but smaller than GNU Guile or Python. However, it is much easier to embed and port than Python or Guile. Lua, but smaller than GNU Guile or Python. However, it is much easier to embed and port than Python or Guile.
## Features Some people use janet for sysadmin scripting, web development, or small video games.
## Language Features
* 600+ functions and macros in the core library * 600+ functions and macros in the core library
* Built-in socket networking, threading, subprocesses, and file system functions. * Built-in socket networking, threading, subprocesses, and file system functions.
* Parsing Expression Grammars (PEG) engine as a more robust Regex alternative * Parsing Expression Grammars (PEG) engine as a more robust Regex alternative
* Macros * Macros and compile-time computation
* Per-thread event loop for efficient IO (epoll/IOCP/kqueue) * Per-thread event loop for efficient IO (epoll/IOCP/kqueue)
* Built-in C FFI lets you load existing binaries and run them. * First-class green threads (continuations) as well as OS threads
* Erlang-style supervision trees that integrate with the event loop * Erlang-style supervision trees that integrate with the event loop
* Configurable at build time - turn features on or off for a smaller or more featureful build
* First-class closures * First-class closures
* Garbage collection * Garbage collection
* First-class green threads (continuations) * Distributed as janet.c and janet.h for embedding into a larger program.
* Python-style generators (implemented as a plain macro) * Python-style generators (implemented as a plain macro)
* Mutable and immutable arrays (array/tuple) * Mutable and immutable arrays (array/tuple)
* Mutable and immutable hashtables (table/struct) * Mutable and immutable hashtables (table/struct)
* Mutable and immutable strings (buffer/string) * Mutable and immutable strings (buffer/string)
* Multithreading * Tail recursion
* Bytecode interpreter with an assembly interface, as well as bytecode verification * Interface with C functions and dynamically load plugins ("natives").
* Tail-call optimization * Built-in C FFI for when the native bindings are too much work
* Interface with C via abstract types and C functions * REPL development with debugger and inspectable runtime
* Dynamically load C libraries
* REPL
* Embedding Janet in other programs
* Interactive environment with detailed stack traces
## Documentation ## Documentation
@ -329,9 +326,7 @@ Gitter provides Matrix and IRC bridges as well.
### How fast is it? ### How fast is it?
Medium speed. It is about the same speed as most interpreted languages without a JIT compiler. Tight, critical
In all seriousness, it is about the same speed as most interpreted languages without a JIT compiler. Tight, critical
loops should probably be written in C or C++ . Programs tend to be a bit faster than loops should probably be written in C or C++ . Programs tend to be a bit faster than
they would be in a language like Python due to the discouragement of slow Object-Oriented abstraction they would be in a language like Python due to the discouragement of slow Object-Oriented abstraction
with lots of hash-table lookups, and making late-binding explicit. All values are boxed in an 8-byte with lots of hash-table lookups, and making late-binding explicit. All values are boxed in an 8-byte

View File

@ -677,6 +677,9 @@ static const SandboxOption sandbox_options[] = {
{"all", JANET_SANDBOX_ALL}, {"all", JANET_SANDBOX_ALL},
{"env", JANET_SANDBOX_ENV}, {"env", JANET_SANDBOX_ENV},
{"ffi", JANET_SANDBOX_FFI}, {"ffi", JANET_SANDBOX_FFI},
{"ffi-define", JANET_SANDBOX_FFI_DEFINE},
{"ffi-jit", JANET_SANDBOX_FFI_JIT},
{"ffi-use", JANET_SANDBOX_FFI_USE},
{"fs", JANET_SANDBOX_FS}, {"fs", JANET_SANDBOX_FS},
{"fs-read", JANET_SANDBOX_FS_READ}, {"fs-read", JANET_SANDBOX_FS_READ},
{"fs-temp", JANET_SANDBOX_FS_TEMP}, {"fs-temp", JANET_SANDBOX_FS_TEMP},
@ -698,6 +701,9 @@ JANET_CORE_FN(janet_core_sandbox,
"* :all - disallow all (except IO to stdout, stderr, and stdin)\n" "* :all - disallow all (except IO to stdout, stderr, and stdin)\n"
"* :env - disallow reading and write env variables\n" "* :env - disallow reading and write env variables\n"
"* :ffi - disallow FFI (recommended if disabling anything else)\n" "* :ffi - disallow FFI (recommended if disabling anything else)\n"
"* :ffi-define - disallow loading new FFI modules and binding new functions\n"
"* :ffi-jit - disallow calling `ffi/jitfn`\n"
"* :ffi-use - disallow using any previously bound FFI functions and memory-unsafe functions.\n"
"* :fs - disallow access to the file system\n" "* :fs - disallow access to the file system\n"
"* :fs-read - disallow read access to the file system\n" "* :fs-read - disallow read access to the file system\n"
"* :fs-temp - disallow creating temporary files\n" "* :fs-temp - disallow creating temporary files\n"

View File

@ -1303,7 +1303,7 @@ JANET_CORE_FN(cfun_ffi_jitfn,
"(ffi/jitfn bytes)", "(ffi/jitfn bytes)",
"Create an abstract type that can be used as the pointer argument to `ffi/call`. The content " "Create an abstract type that can be used as the pointer argument to `ffi/call`. The content "
"of `bytes` is architecture specific machine code that will be copied into executable memory.") { "of `bytes` is architecture specific machine code that will be copied into executable memory.") {
janet_sandbox_assert(JANET_SANDBOX_FFI); janet_sandbox_assert(JANET_SANDBOX_FFI_JIT);
janet_fixarity(argc, 1); janet_fixarity(argc, 1);
JanetByteView bytes = janet_getbytes(argv, 0); JanetByteView bytes = janet_getbytes(argv, 0);
@ -1356,7 +1356,7 @@ JANET_CORE_FN(cfun_ffi_call,
"(ffi/call pointer signature & args)", "(ffi/call pointer signature & args)",
"Call a raw pointer as a function pointer. The function signature specifies " "Call a raw pointer as a function pointer. The function signature specifies "
"how Janet values in `args` are converted to native machine types.") { "how Janet values in `args` are converted to native machine types.") {
janet_sandbox_assert(JANET_SANDBOX_FFI); janet_sandbox_assert(JANET_SANDBOX_FFI_USE);
janet_arity(argc, 2, -1); janet_arity(argc, 2, -1);
void *function_pointer = janet_ffi_get_callable_pointer(argv, 0); void *function_pointer = janet_ffi_get_callable_pointer(argv, 0);
JanetFFISignature *signature = janet_getabstract(argv, 1, &janet_signature_type); JanetFFISignature *signature = janet_getabstract(argv, 1, &janet_signature_type);
@ -1381,7 +1381,7 @@ JANET_CORE_FN(cfun_ffi_buffer_write,
"Append a native type to a buffer such as it would appear in memory. This can be used " "Append a native type to a buffer such as it would appear in memory. This can be used "
"to pass pointers to structs in the ffi, or send C/C++/native structs over the network " "to pass pointers to structs in the ffi, or send C/C++/native structs over the network "
"or to files. Returns a modifed buffer or a new buffer if one is not supplied.") { "or to files. Returns a modifed buffer or a new buffer if one is not supplied.") {
janet_sandbox_assert(JANET_SANDBOX_FFI); janet_sandbox_assert(JANET_SANDBOX_FFI_USE);
janet_arity(argc, 2, 4); janet_arity(argc, 2, 4);
JanetFFIType type = decode_ffi_type(argv[0]); JanetFFIType type = decode_ffi_type(argv[0]);
uint32_t el_size = (uint32_t) type_size(type); uint32_t el_size = (uint32_t) type_size(type);
@ -1404,7 +1404,7 @@ JANET_CORE_FN(cfun_ffi_buffer_read,
"Parse a native struct out of a buffer and convert it to normal Janet data structures. " "Parse a native struct out of a buffer and convert it to normal Janet data structures. "
"This function is the inverse of `ffi/write`. `bytes` can also be a raw pointer, although " "This function is the inverse of `ffi/write`. `bytes` can also be a raw pointer, although "
"this is unsafe.") { "this is unsafe.") {
janet_sandbox_assert(JANET_SANDBOX_FFI); janet_sandbox_assert(JANET_SANDBOX_FFI_USE);
janet_arity(argc, 2, 3); janet_arity(argc, 2, 3);
JanetFFIType type = decode_ffi_type(argv[0]); JanetFFIType type = decode_ffi_type(argv[0]);
size_t offset = (size_t) janet_optnat(argv, argc, 2, 0); size_t offset = (size_t) janet_optnat(argv, argc, 2, 0);
@ -1451,7 +1451,7 @@ JANET_CORE_FN(janet_core_raw_native,
" or run any code from it. This is different than `native`, which will " " or run any code from it. This is different than `native`, which will "
"run initialization code to get a module table. If `path` is nil, opens the current running binary. " "run initialization code to get a module table. If `path` is nil, opens the current running binary. "
"Returns a `core/native`.") { "Returns a `core/native`.") {
janet_sandbox_assert(JANET_SANDBOX_FFI); janet_sandbox_assert(JANET_SANDBOX_FFI_DEFINE);
janet_arity(argc, 0, 1); janet_arity(argc, 0, 1);
const char *path = janet_optcstring(argv, argc, 0, NULL); const char *path = janet_optcstring(argv, argc, 0, NULL);
Clib lib = load_clib(path); Clib lib = load_clib(path);
@ -1467,7 +1467,7 @@ JANET_CORE_FN(janet_core_native_lookup,
"(ffi/lookup native symbol-name)", "(ffi/lookup native symbol-name)",
"Lookup a symbol from a native object. All symbol lookups will return a raw pointer " "Lookup a symbol from a native object. All symbol lookups will return a raw pointer "
"if the symbol is found, else nil.") { "if the symbol is found, else nil.") {
janet_sandbox_assert(JANET_SANDBOX_FFI); janet_sandbox_assert(JANET_SANDBOX_FFI_DEFINE);
janet_fixarity(argc, 2); janet_fixarity(argc, 2);
JanetAbstractNative *anative = janet_getabstract(argv, 0, &janet_native_type); JanetAbstractNative *anative = janet_getabstract(argv, 0, &janet_native_type);
const char *sym = janet_getcstring(argv, 1); const char *sym = janet_getcstring(argv, 1);
@ -1481,7 +1481,7 @@ JANET_CORE_FN(janet_core_native_close,
"(ffi/close native)", "(ffi/close native)",
"Free a native object. Dereferencing pointers to symbols in the object will have undefined " "Free a native object. Dereferencing pointers to symbols in the object will have undefined "
"behavior after freeing.") { "behavior after freeing.") {
janet_sandbox_assert(JANET_SANDBOX_FFI); janet_sandbox_assert(JANET_SANDBOX_FFI_DEFINE);
janet_fixarity(argc, 1); janet_fixarity(argc, 1);
JanetAbstractNative *anative = janet_getabstract(argv, 0, &janet_native_type); JanetAbstractNative *anative = janet_getabstract(argv, 0, &janet_native_type);
if (anative->closed) janet_panic("native object already closed"); if (anative->closed) janet_panic("native object already closed");
@ -1494,7 +1494,7 @@ JANET_CORE_FN(janet_core_native_close,
JANET_CORE_FN(cfun_ffi_malloc, JANET_CORE_FN(cfun_ffi_malloc,
"(ffi/malloc size)", "(ffi/malloc size)",
"Allocates memory directly using the janet memory allocator. Memory allocated in this way must be freed manually! Returns a raw pointer, or nil if size = 0.") { "Allocates memory directly using the janet memory allocator. Memory allocated in this way must be freed manually! Returns a raw pointer, or nil if size = 0.") {
janet_sandbox_assert(JANET_SANDBOX_FFI); janet_sandbox_assert(JANET_SANDBOX_FFI_USE);
janet_fixarity(argc, 1); janet_fixarity(argc, 1);
size_t size = janet_getsize(argv, 0); size_t size = janet_getsize(argv, 0);
if (size == 0) return janet_wrap_nil(); if (size == 0) return janet_wrap_nil();
@ -1504,7 +1504,7 @@ JANET_CORE_FN(cfun_ffi_malloc,
JANET_CORE_FN(cfun_ffi_free, JANET_CORE_FN(cfun_ffi_free,
"(ffi/free pointer)", "(ffi/free pointer)",
"Free memory allocated with `ffi/malloc`. Returns nil.") { "Free memory allocated with `ffi/malloc`. Returns nil.") {
janet_sandbox_assert(JANET_SANDBOX_FFI); janet_sandbox_assert(JANET_SANDBOX_FFI_USE);
janet_fixarity(argc, 1); janet_fixarity(argc, 1);
if (janet_checktype(argv[0], JANET_NIL)) return janet_wrap_nil(); if (janet_checktype(argv[0], JANET_NIL)) return janet_wrap_nil();
void *pointer = janet_getpointer(argv, 0); void *pointer = janet_getpointer(argv, 0);
@ -1519,7 +1519,7 @@ JANET_CORE_FN(cfun_ffi_pointer_buffer,
"to be manipulated with buffer functions. Attempts to resize or extend the buffer " "to be manipulated with buffer functions. Attempts to resize or extend the buffer "
"beyond its initial capacity will raise an error. As with many FFI functions, this is memory " "beyond its initial capacity will raise an error. As with many FFI functions, this is memory "
"unsafe and can potentially allow out of bounds memory access. Returns a new buffer.") { "unsafe and can potentially allow out of bounds memory access. Returns a new buffer.") {
janet_sandbox_assert(JANET_SANDBOX_FFI); janet_sandbox_assert(JANET_SANDBOX_FFI_USE);
janet_arity(argc, 2, 4); janet_arity(argc, 2, 4);
void *pointer = janet_getpointer(argv, 0); void *pointer = janet_getpointer(argv, 0);
int32_t capacity = janet_getnat(argv, 1); int32_t capacity = janet_getnat(argv, 1);

View File

@ -1809,13 +1809,16 @@ JANET_API void janet_stacktrace_ext(JanetFiber *fiber, Janet err, const char *pr
#define JANET_SANDBOX_SUBPROCESS 2 #define JANET_SANDBOX_SUBPROCESS 2
#define JANET_SANDBOX_NET_CONNECT 4 #define JANET_SANDBOX_NET_CONNECT 4
#define JANET_SANDBOX_NET_LISTEN 8 #define JANET_SANDBOX_NET_LISTEN 8
#define JANET_SANDBOX_FFI 16 #define JANET_SANDBOX_FFI_DEFINE 16
#define JANET_SANDBOX_FS_WRITE 32 #define JANET_SANDBOX_FS_WRITE 32
#define JANET_SANDBOX_FS_READ 64 #define JANET_SANDBOX_FS_READ 64
#define JANET_SANDBOX_HRTIME 128 #define JANET_SANDBOX_HRTIME 128
#define JANET_SANDBOX_ENV 256 #define JANET_SANDBOX_ENV 256
#define JANET_SANDBOX_DYNAMIC_MODULES 512 #define JANET_SANDBOX_DYNAMIC_MODULES 512
#define JANET_SANDBOX_FS_TEMP 1024 #define JANET_SANDBOX_FS_TEMP 1024
#define JANET_SANDBOX_FFI_USE 2048
#define JANET_SANDBOX_FFI_JIT 4096
#define JANET_SANDBOX_FFI (JANET_SANDBOX_FFI_DEFINE | JANET_SANDBOX_FFI_USE | JANET_SANDBOX_FFI_JIT)
#define JANET_SANDBOX_FS (JANET_SANDBOX_FS_WRITE | JANET_SANDBOX_FS_READ | JANET_SANDBOX_FS_TEMP) #define JANET_SANDBOX_FS (JANET_SANDBOX_FS_WRITE | JANET_SANDBOX_FS_READ | JANET_SANDBOX_FS_TEMP)
#define JANET_SANDBOX_NET (JANET_SANDBOX_NET_CONNECT | JANET_SANDBOX_NET_LISTEN) #define JANET_SANDBOX_NET (JANET_SANDBOX_NET_CONNECT | JANET_SANDBOX_NET_LISTEN)
#define JANET_SANDBOX_ALL (UINT32_MAX) #define JANET_SANDBOX_ALL (UINT32_MAX)