mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 12:01:16 +00:00
Add defaults to compare_exchange, small fix in printf. Disallow obviously wrong code that returns the pointer to a variable on the stack.
This commit is contained in:
@@ -54,12 +54,12 @@ macro void @atomic_store(&x, value, AtomicOrdering $ordering = SEQ_CONSISTENT, $
|
||||
$$atomic_store(&x, value, $volatile, (int)$ordering);
|
||||
}
|
||||
|
||||
macro compare_exchange(ptr, compare, value, AtomicOrdering $success, AtomicOrdering $failure, bool $volatile = true, bool $weak = false, usz $alignment = 0)
|
||||
macro compare_exchange(ptr, compare, value, AtomicOrdering $success = SEQ_CONSISTENT, AtomicOrdering $failure = SEQ_CONSISTENT, bool $volatile = true, bool $weak = false, usz $alignment = 0)
|
||||
{
|
||||
return $$compare_exchange(ptr, compare, value, $volatile, $weak, $success.ordinal, $failure.ordinal, $alignment);
|
||||
}
|
||||
|
||||
macro compare_exchange_volatile(ptr, compare, value, AtomicOrdering $success, AtomicOrdering $failure)
|
||||
macro compare_exchange_volatile(ptr, compare, value, AtomicOrdering $success = SEQ_CONSISTENT, AtomicOrdering $failure = SEQ_CONSISTENT)
|
||||
{
|
||||
return compare_exchange(ptr, compare, value, $success, $failure, true);
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ fn char[]! bprintf(char[] buffer, String format, args...) @maydiscard
|
||||
BufferData data = { .buffer = buffer };
|
||||
formatter.init(&out_buffer_fn, &data);
|
||||
usz size = formatter.vprintf(format, args)?;
|
||||
return buffer[:size];
|
||||
return buffer[:data.written];
|
||||
}
|
||||
|
||||
fn usz! File.printf(File file, String format, args...) @maydiscard
|
||||
|
||||
@@ -6,4 +6,40 @@ fault NetError
|
||||
URL_TOO_LONG,
|
||||
INVALID_SOCKET,
|
||||
GENERAL_ERROR,
|
||||
INVALID_IP_STRING,
|
||||
}
|
||||
|
||||
fn uint! ipv4toint(String s)
|
||||
{
|
||||
uint out;
|
||||
int element;
|
||||
int current = -1;
|
||||
foreach (c : s)
|
||||
{
|
||||
if (c == '.')
|
||||
{
|
||||
if (current < 0) return NetError.INVALID_IP_STRING!;
|
||||
out = out << 8 + current;
|
||||
current = -1;
|
||||
element++;
|
||||
continue;
|
||||
}
|
||||
if (element > 3 || c < '0' || c > '9') return NetError.INVALID_IP_STRING!;
|
||||
if (current < 0)
|
||||
{
|
||||
current = c - '0';
|
||||
continue;
|
||||
}
|
||||
current = current * 10 + c - '0';
|
||||
}
|
||||
if (element != 3 || current < 0) return NetError.INVALID_IP_STRING!;
|
||||
out = out << 8 + current;
|
||||
return out;
|
||||
}
|
||||
|
||||
fn String! inttoipv4(uint val, Allocator* allocator = mem::current_allocator())
|
||||
{
|
||||
char[3 * 4 + 3 + 1] buffer;
|
||||
String res = (String)io::bprintf(&buffer, "%d.%d.%d.%d", val >> 24, (val >> 16) & 0xFF, (val >> 8) & 0xFF, val & 0xFF)?;
|
||||
return res.copyz(allocator);
|
||||
}
|
||||
@@ -353,6 +353,46 @@ static inline bool sema_analyse_block_exit_stmt(SemaContext *context, Ast *state
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevent the common mistake of `return &a` where "a" is a local.
|
||||
* @return true if the check is ok (no such escape)
|
||||
*/
|
||||
INLINE bool sema_check_not_stack_variable_escape(SemaContext *context, Expr *expr)
|
||||
{
|
||||
Expr *outer = expr;
|
||||
while (expr->expr_kind == EXPR_CAST) expr = exprptr(expr->cast_expr.expr);
|
||||
// We only want && and &
|
||||
if (expr->expr_kind == EXPR_SUBSCRIPT_ADDR)
|
||||
{
|
||||
expr = exprptr(expr->subscript_expr.expr);
|
||||
goto CHECK_ACCESS;
|
||||
}
|
||||
if (expr->expr_kind != EXPR_UNARY) return true;
|
||||
if (expr->unary_expr.operator == UNARYOP_TADDR)
|
||||
{
|
||||
SEMA_ERROR(outer, "A pointer to a temporary value will be invalid once the function returns. Try copying the value to the heap or the temp memory instead.");
|
||||
return false;
|
||||
}
|
||||
if (expr->unary_expr.operator != UNARYOP_ADDR) return true;
|
||||
expr = expr->unary_expr.expr;
|
||||
CHECK_ACCESS:
|
||||
while (expr->expr_kind == EXPR_ACCESS) expr = expr->access_expr.parent;
|
||||
if (expr->expr_kind != EXPR_IDENTIFIER) return true;
|
||||
Decl *decl = expr->identifier_expr.decl;
|
||||
if (decl->decl_kind != DECL_VAR) return true;
|
||||
switch (decl->var.kind)
|
||||
{
|
||||
case VARDECL_LOCAL:
|
||||
case VARDECL_PARAM:
|
||||
break;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
SEMA_ERROR(outer, "A pointer to a local variable will be invalid once the function returns. "
|
||||
"Allocate the data on the heap or temp memory to return a pointer.");
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* We have the following possibilities:
|
||||
* 1. return
|
||||
@@ -389,6 +429,7 @@ static inline bool sema_analyse_return_stmt(SemaContext *context, Ast *statement
|
||||
if (return_expr)
|
||||
{
|
||||
if (!sema_analyse_expr_rhs(context, expected_rtype, return_expr, type_is_optional(expected_rtype))) return false;
|
||||
if (!sema_check_not_stack_variable_escape(context, return_expr)) return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -1 +1 @@
|
||||
#define COMPILER_VERSION "0.4.73"
|
||||
#define COMPILER_VERSION "0.4.74"
|
||||
40
test/test_suite/safe/detect_invalid_deref_return.c3
Normal file
40
test/test_suite/safe/detect_invalid_deref_return.c3
Normal file
@@ -0,0 +1,40 @@
|
||||
fn int[] test1()
|
||||
{
|
||||
int[3] x;
|
||||
return &x; // #error: local variable will be invalid
|
||||
}
|
||||
|
||||
fn int* test2()
|
||||
{
|
||||
int[3] x;
|
||||
return &x[0]; // #error: local variable will be invalid
|
||||
}
|
||||
|
||||
struct Abc
|
||||
{
|
||||
int a;
|
||||
int[3] b;
|
||||
}
|
||||
|
||||
fn int* test3()
|
||||
{
|
||||
Abc x;
|
||||
return &x.a; // #error: local variable will be invalid
|
||||
}
|
||||
|
||||
fn int* test4()
|
||||
{
|
||||
Abc x;
|
||||
return &x.b; // #error: local variable will be invalid
|
||||
}
|
||||
|
||||
fn int* test5()
|
||||
{
|
||||
Abc x;
|
||||
return &x.b[0]; // #error: local variable will be invalid
|
||||
}
|
||||
|
||||
fn int* test6()
|
||||
{
|
||||
return &&1; // #error: temporary value will
|
||||
}
|
||||
Reference in New Issue
Block a user