Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Missing parapoly variable in argument of size_of results in bad codegen #4785

Open
zen3ger opened this issue Feb 2, 2025 · 0 comments
Open

Comments

@zen3ger
Copy link
Contributor

zen3ger commented Feb 2, 2025

Context

Please provide any relevant information about your setup. This is important in case the issue is not reproducible except for under certain conditions.

	Odin:    dev-2025-01:20fa9fbd6
	OS:      openSUSE Tumbleweed, Linux 6.12.9-1-default
	CPU:     AMD Ryzen 9 5900X 12-Core Processor            
	RAM:     31999 MiB
	Backend: LLVM 19.1.6

Expected Behavior

Compiler error on missing parapoly var.

Current Behavior

No compiler error, generated code results in memory corruption.

Failure Information (for bugs)

Please help provide information about the failure if this is a bug. If it is not a bug, please remove the rest of this template.

Steps to Reproduce

Run the following example with odin test:

package foo

import "base:runtime"
import "core:testing"

Block :: struct ($T: typeid) {
	len:  int,
	cap:  int,
	data: [0]T,
}

new_block_bad :: proc($T: typeid, cap: int, allocator := context.allocator) -> ^Block(T) {
	// missed parapoly type in `size_of(Block)`, compiles and generated code causes consecutive allocs
	// to corrupt memory of another block on free
	bytes, err := runtime.mem_alloc(size_of(Block) + size_of(T) * cap, align_of(Block(T)), allocator)
	//                              ^^^^^^^^^^^^^^
	assert(err == nil)
	block := cast(^Block(T))raw_data(bytes)
	block.cap = cap
	return block
}

new_block_ok :: proc($T: typeid, cap: int, allocator := context.allocator) -> ^Block(T) {
	bytes, err := runtime.mem_alloc(size_of(Block(T)) + size_of(T) * cap, align_of(Block(T)), allocator)
	assert(err == nil)
	block := cast(^Block(T))raw_data(bytes)
	block.cap = cap
	return block
}

free_block :: proc(block: ^Block($T), allocator := context.allocator) {
	runtime.mem_free(block, allocator)
}

block_to_slice :: proc(block: ^Block($T)) -> []T {
	return (cast([^]T)&block.data)[:block.cap]
}

CANARY :: 0xAAAA_AAAA

@(test)
test_bad_alloc :: proc(t: ^testing.T) {
	b1 := new_block_bad(uint, 7)
	b2 := new_block_bad(uint, 8)

	s1 := block_to_slice(b1)
	for &u in s1 {
		u = CANARY
	}
	s2 := block_to_slice(b2)
	for &u in s2 {
		u = CANARY
	}
	free_block(b2)
	for u in s1 {
		testing.expectf(t, u == CANARY, "corrupt %v -> %v", CANARY, u)
	}
	free_block(b1)
}


@(test)
test_ok_alloc :: proc(t: ^testing.T) {
	b1 := new_block_ok(uint, 7)
	b2 := new_block_ok(uint, 8)

	s1 := block_to_slice(b1)
	for &u in s1 {
		u = CANARY
	}
	s2 := block_to_slice(b2)
	for &u in s2 {
		u = CANARY
	}
	free_block(b2)
	for u in s1 {
		testing.expectf(t, u == CANARY, "corrupt %v -> %v", CANARY, u)
	}
	free_block(b1)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant