Commit 078f8203 authored by Paul Asmuth's avatar Paul Asmuth
Browse files

add runtime sanity checks

In contrast to most of the other parts of the codebase, the "sexpr"
utility and manipulation code deals with a lot of raw pointers. This
change adds a number of runtime assertions and null pointer checks
so that potential future bugs in consumers of the API will trigger
an exception instead of generating invalid memory reads or writes.

Still, correct could should never trigger any of the newly added
runtime errors; every thrown exception is considered a bug.
parent 4a24ae3a
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -168,6 +168,10 @@ ReturnCode color_map_read_gradient(
    }

    gradient.emplace_back(step, color);

    if (!expr_has_next(expr)) {
      break;
    }
  }

  *color_map = color_map_gradient(gradient);
@@ -207,6 +211,10 @@ ReturnCode color_map_read_steps(
    }

    steps.emplace_back(step, color);

    if (!expr_has_next(expr)) {
      break;
    }
  }

  *color_map = color_map_steps(steps);
+4 −0
Original line number Diff line number Diff line
@@ -66,6 +66,10 @@ ReturnCode eval(
    if (auto rc = cmd->fn(ctx, args); !rc) {
      return rc;
    }

    if (!expr_has_next(expr)) {
      break;
    }
  }

  return OK;
+48 −1
Original line number Diff line number Diff line
@@ -34,6 +34,10 @@ struct Expr {
};

void expr_destroy(Expr* expr) {
  if (!expr) {
    throw std::runtime_error("invalid expression");
  }

  delete expr;
}

@@ -65,18 +69,42 @@ ExprStorage expr_create_value_literal(const std::string& str) {
}

Expr* expr_next(Expr* expr) {
  if (!expr) {
    throw std::runtime_error("invalid expression");
  }

  return expr->next.get();
}

const Expr* expr_next(const Expr* expr) {
  if (!expr) {
    throw std::runtime_error("invalid expression");
  }

  return expr->next.get();
}

bool expr_has_next(const Expr* expr) {
  if (!expr) {
    throw std::runtime_error("invalid expression");
  }

  return !!expr->next;
}

void expr_set_next(Expr* expr, ExprStorage next) {
  if (!expr) {
    throw std::runtime_error("invalid expression");
  }

  expr->next = std::move(next);
}

ExprStorage* expr_get_next_storage(Expr* expr) {
  if (!expr) {
    throw std::runtime_error("invalid expression");
  }

  return &expr->next;
}

@@ -89,10 +117,18 @@ bool expr_is_list(const Expr* expr, const std::string& head) {
}

const Expr* expr_get_list(const Expr* expr) {
  if (!expr || expr->type != ExprType::LIST) {
    throw std::runtime_error("invalid expression (not a list)");
  }

  return expr->list.get();
}

const Expr* expr_get_list_tail(const Expr* expr) {
  if (!expr || expr->type != ExprType::LIST) {
    throw std::runtime_error("invalid expression (not a list)");
  }

  if (auto l = expr->list.get(); l) {
    return expr_next(l);
  } else {
@@ -101,6 +137,10 @@ const Expr* expr_get_list_tail(const Expr* expr) {
}

ExprStorage* expr_get_list_storage(Expr* expr) {
  if (!expr) {
    throw std::runtime_error("invalid expression");
  }

  return &expr->list;
}

@@ -129,6 +169,10 @@ bool expr_is_value_quoted(const Expr* expr, const std::string& cmp) {
}

const std::string& expr_get_value(const Expr* expr) {
  if (!expr) {
    throw std::runtime_error("invalid expression");
  }

  return expr->value;
}

@@ -153,8 +197,11 @@ ExprStorage expr_clone(const Expr* e, int count /* =-1 */) {
}

std::string expr_inspect(const Expr* expr) {
  std::stringstream s;
  if (!expr) {
    return "<null>";
  }

  std::stringstream s;
  switch (expr->type) {
    case ExprType::VALUE:
    case ExprType::VALUE_LITERAL:
+1 −0
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@ ExprStorage expr_create_value_literal(const std::string& str);

Expr* expr_next(Expr* expr);
const Expr* expr_next(const Expr* expr);
bool expr_has_next(const Expr* expr);
void expr_set_next(Expr* expr, ExprStorage next);
ExprStorage* expr_get_next_storage(Expr* expr);

+11 −12
Original line number Diff line number Diff line
@@ -108,19 +108,18 @@ ReturnCode expr_to_float64_opt_pair(
        expr_inspect(expr));
  }

  for (size_t i = 0; i < 2; ++i) {
    if (!expr || !expr_is_value(expr)) {
      return errorf(
          ERROR,
          "argument error; expected a value, got: {}",
          expr_inspect(expr));
  const auto& args = expr_collect(expr);

  if (args.size() != 2) {
    return err_invalid_nargs(args.size(), 2);
  }

    if (auto rc = expr_to_float64_opt(expr, i == 0 ? v1 : v2); !rc) {
  if (auto rc = expr_to_float64_opt(args[0], v1); !rc) {
    return rc;
  }

    expr = expr_next(expr);
  if (auto rc = expr_to_float64_opt(args[1], v2); !rc) {
    return rc;
  }

  return OK;
Loading