diff --git a/lib/std/core/mem.c3 b/lib/std/core/mem.c3 index cab206cbb..57c638947 100644 --- a/lib/std/core/mem.c3 +++ b/lib/std/core/mem.c3 @@ -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); } diff --git a/lib/std/io/io_printf.c3 b/lib/std/io/io_printf.c3 index f4790bc5c..8e8f08be5 100644 --- a/lib/std/io/io_printf.c3 +++ b/lib/std/io/io_printf.c3 @@ -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 diff --git a/lib/std/net/net.c3 b/lib/std/net/net.c3 index d26c1e793..8ad8b4761 100644 --- a/lib/std/net/net.c3 +++ b/lib/std/net/net.c3 @@ -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); } \ No newline at end of file diff --git a/src/compiler/sema_stmts.c b/src/compiler/sema_stmts.c index 7e22ca89c..9d2f6d14e 100644 --- a/src/compiler/sema_stmts.c +++ b/src/compiler/sema_stmts.c @@ -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 { diff --git a/src/version.h b/src/version.h index b11c77a21..3ce5af12d 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define COMPILER_VERSION "0.4.73" \ No newline at end of file +#define COMPILER_VERSION "0.4.74" \ No newline at end of file diff --git a/test/test_suite/safe/detect_invalid_deref_return.c3 b/test/test_suite/safe/detect_invalid_deref_return.c3 new file mode 100644 index 000000000..f46ae7540 --- /dev/null +++ b/test/test_suite/safe/detect_invalid_deref_return.c3 @@ -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 +}