Safeguard against ei_* failures

This commit is contained in:
Jordan Bracco 2020-06-14 18:21:17 +02:00
parent cca911b1b7
commit 1d949c82ca
3 changed files with 80 additions and 68 deletions

View file

@ -198,6 +198,8 @@ defmodule GenMagic.Server do
1 -> :no_database 1 -> :no_database
2 -> :no_argument 2 -> :no_argument
3 -> :missing_database 3 -> :missing_database
4 -> :ei_alloc_failed
5 -> :ei_bad_term
code -> {:unexpected_error, code} code -> {:unexpected_error, code}
end end

View file

@ -58,8 +58,8 @@
#define ERROR_NO_DATABASE 1 #define ERROR_NO_DATABASE 1
#define ERROR_NO_ARGUMENT 2 #define ERROR_NO_ARGUMENT 2
#define ERROR_MISSING_DATABASE 3 #define ERROR_MISSING_DATABASE 3
#define ERROR_BAD_TERM 4 #define ERROR_EI 4
#define ERROR_EI 5 #define ERROR_BAD_TERM 5
// We use a bigger than possible valid command length (around 4111 bytes) to // We use a bigger than possible valid command length (around 4111 bytes) to
// allow more precise errors when using too long paths. // allow more precise errors when using too long paths.
@ -69,6 +69,13 @@
#define MAGIC_FLAGS_COMMON (MAGIC_CHECK | MAGIC_ERROR) #define MAGIC_FLAGS_COMMON (MAGIC_CHECK | MAGIC_ERROR)
magic_t magic_setup(int flags); magic_t magic_setup(int flags);
#define EI_ENSURE(result) \
do { \
if (result != 0) { \
exit(ERROR_EI); \
} \
} while (0);
typedef char byte; typedef char byte;
void setup_environment(); void setup_environment();
@ -103,11 +110,10 @@ int main(int argc, char **argv) {
setup_system(); setup_system();
ei_x_buff ok_buf; ei_x_buff ok_buf;
if (ei_x_new_with_version(&ok_buf) || ei_x_encode_atom(&ok_buf, "ready")) EI_ENSURE(ei_x_new_with_version(&ok_buf));
exit(ERROR_EI); EI_ENSURE(ei_x_encode_atom(&ok_buf, "ready"));
write_cmd(ok_buf.buff, ok_buf.index); write_cmd(ok_buf.buff, ok_buf.index);
if (ei_x_free(&ok_buf) != 0) EI_ENSURE(ei_x_free(&ok_buf));
exit(ERROR_EI);
byte buf[COMMAND_BUFFER_SIZE]; byte buf[COMMAND_BUFFER_SIZE];
uint16_t len; uint16_t len;
@ -125,9 +131,8 @@ int process_command(uint16_t len, byte *buf) {
index = 0; index = 0;
// Initialize result // Initialize result
if (ei_x_new_with_version(&result) || ei_x_encode_tuple_header(&result, 2)) { EI_ENSURE(ei_x_new_with_version(&result));
exit(ERROR_EI); EI_ENSURE(ei_x_encode_tuple_header(&result, 2));
}
if (len >= COMMAND_LEN) { if (len >= COMMAND_LEN) {
error(&result, "badarg"); error(&result, "badarg");
@ -160,7 +165,7 @@ int process_command(uint16_t len, byte *buf) {
if (termtype == ERL_BINARY_EXT) { if (termtype == ERL_BINARY_EXT) {
if (termsize < 4096) { if (termsize < 4096) {
long bin_length; long bin_length;
ei_decode_binary(buf, &index, path, &bin_length); EI_ENSURE(ei_decode_binary(buf, &index, path, &bin_length));
path[termsize] = '\0'; path[termsize] = '\0';
process_file(path, &result); process_file(path, &result);
} else { } else {
@ -175,11 +180,11 @@ int process_command(uint16_t len, byte *buf) {
int termtype; int termtype;
int termsize; int termsize;
char bytes[51]; char bytes[51];
ei_get_type(buf, &index, &termtype, &termsize); EI_ENSURE(ei_get_type(buf, &index, &termtype, &termsize));
if (termtype == ERL_BINARY_EXT && termsize < 50) { if (termtype == ERL_BINARY_EXT && termsize < 50) {
long bin_length; long bin_length;
ei_decode_binary(buf, &index, bytes, &bin_length); EI_ENSURE(ei_decode_binary(buf, &index, bytes, &bin_length));
bytes[termsize] = '\0'; bytes[termsize] = '\0';
process_bytes(bytes, termsize, &result); process_bytes(bytes, termsize, &result);
} else { } else {
@ -195,9 +200,7 @@ int process_command(uint16_t len, byte *buf) {
write_cmd(result.buff, result.index); write_cmd(result.buff, result.index);
if (ei_x_free(&result) != 0) { EI_ENSURE(ei_x_free(&result));
exit(ERROR_EI);
}
return 0; return 0;
} }
@ -254,7 +257,6 @@ void setup_options_file(char *optarg) {
} }
void setup_options_default() { void setup_options_default() {
struct magic_file *next = malloc(sizeof(struct magic_file)); struct magic_file *next = malloc(sizeof(struct magic_file));
next->path = NULL; next->path = NULL;
next->prev = magic_database; next->prev = magic_database;
@ -314,22 +316,24 @@ void process_bytes(char *path, int size, ei_x_buff *result) {
return; return;
} }
ei_x_encode_atom(result, "ok"); EI_ENSURE(ei_x_encode_atom(result, "ok"));
ei_x_encode_tuple_header(result, 3); EI_ENSURE(ei_x_encode_tuple_header(result, 3));
ei_x_encode_binary(result, mime_type_result, strlen(mime_type_result)); EI_ENSURE(
ei_x_encode_binary(result, mime_encoding_result, ei_x_encode_binary(result, mime_type_result, strlen(mime_type_result)));
strlen(mime_encoding_result)); EI_ENSURE(ei_x_encode_binary(result, mime_encoding_result,
ei_x_encode_binary(result, type_name_result, strlen(type_name_result)); strlen(mime_encoding_result)));
EI_ENSURE(
ei_x_encode_binary(result, type_name_result, strlen(type_name_result)));
return; return;
} }
void handle_magic_error(magic_t handle, int errn, ei_x_buff *result) { void handle_magic_error(magic_t handle, int errn, ei_x_buff *result) {
const char *error = magic_error(handle); const char *error = magic_error(handle);
ei_x_encode_atom(result, "error"); EI_ENSURE(ei_x_encode_atom(result, "error"));
ei_x_encode_tuple_header(result, 2); EI_ENSURE(ei_x_encode_tuple_header(result, 2));
long errlon = (long)errn; long errlon = (long)errn;
ei_x_encode_long(result, errlon); EI_ENSURE(ei_x_encode_long(result, errlon));
ei_x_encode_binary(result, error, strlen(error)); EI_ENSURE(ei_x_encode_binary(result, error, strlen(error)));
return; return;
} }
@ -358,12 +362,14 @@ void process_file(char *path, ei_x_buff *result) {
return; return;
} }
ei_x_encode_atom(result, "ok"); EI_ENSURE(ei_x_encode_atom(result, "ok"));
ei_x_encode_tuple_header(result, 3); EI_ENSURE(ei_x_encode_tuple_header(result, 3));
ei_x_encode_binary(result, mime_type_result, strlen(mime_type_result)); EI_ENSURE(
ei_x_encode_binary(result, mime_encoding_result, ei_x_encode_binary(result, mime_type_result, strlen(mime_type_result)));
strlen(mime_encoding_result)); EI_ENSURE(ei_x_encode_binary(result, mime_encoding_result,
ei_x_encode_binary(result, type_name_result, strlen(type_name_result)); strlen(mime_encoding_result)));
EI_ENSURE(
ei_x_encode_binary(result, type_name_result, strlen(type_name_result)));
return; return;
} }
@ -428,12 +434,10 @@ size_t write_cmd(byte *buf, size_t len) {
} }
void error(ei_x_buff *result, const char *error) { void error(ei_x_buff *result, const char *error) {
ei_x_encode_atom(result, "error"); EI_ENSURE(ei_x_encode_atom(result, "error"));
ei_x_encode_atom(result, error); EI_ENSURE(ei_x_encode_atom(result, error));
write_cmd(result->buff, result->index); write_cmd(result->buff, result->index);
EI_ENSURE(ei_x_free(result));
if (ei_x_free(result) != 0)
exit(ERROR_EI);
} }
void fdseek(uint16_t count) { void fdseek(uint16_t count) {

View file

@ -2,6 +2,7 @@ defmodule GenMagic.ApprenticeTest do
use GenMagic.MagicCase use GenMagic.MagicCase
@tmp_path "/tmp/testgenmagicx" @tmp_path "/tmp/testgenmagicx"
require Logger
test "sends ready" do test "sends ready" do
port = Port.open(GenMagic.Config.get_port_name(), GenMagic.Config.get_port_options([])) port = Port.open(GenMagic.Config.get_port_name(), GenMagic.Config.get_port_options([]))
@ -43,7 +44,7 @@ defmodule GenMagic.ApprenticeTest do
test "exits with badly formatted erlang terms", %{port: port} do test "exits with badly formatted erlang terms", %{port: port} do
send(port, {self(), {:command, "i forgot to term_to_binary!!"}}) send(port, {self(), {:command, "i forgot to term_to_binary!!"}})
assert_receive {^port, {:exit_status, 4}} assert_receive {^port, {:exit_status, 5}}
end end
test "errors with wrong command", %{port: port} do test "errors with wrong command", %{port: port} do
@ -89,7 +90,8 @@ defmodule GenMagic.ApprenticeTest do
test "works with big file path", %{port: port} do test "works with big file path", %{port: port} do
# Test with longest valid path. # Test with longest valid path.
{dir, bigfile} = too_big(@tmp_path, "/a") {dir, bigfile} = too_big(@tmp_path, "/a")
File.mkdir_p!(dir) case File.mkdir_p(dir) do
:ok ->
File.touch!(bigfile) File.touch!(bigfile)
on_exit(fn -> File.rm_rf!(@tmp_path) end) on_exit(fn -> File.rm_rf!(@tmp_path) end)
send(port, {self(), {:command, :erlang.term_to_binary({:file, bigfile})}}) send(port, {self(), {:command, :erlang.term_to_binary({:file, bigfile})}})
@ -120,6 +122,10 @@ defmodule GenMagic.ApprenticeTest do
assert_receive {^port, {:data, data}} assert_receive {^port, {:data, data}}
assert {:ok, _} = :erlang.binary_to_term(data) assert {:ok, _} = :erlang.binary_to_term(data)
refute_receive _ refute_receive _
{:error, :enametoolong} ->
Logger.info("Skipping test, operating system does not support max POSIX length for directories")
:ignore
end
end end
end end