Skip to content

Commit 951bef4

Browse files
authored
Merge pull request #4907 from Feoramund/os2-fix-env-linux
Fix data races in `os2/env_linux.odin`
2 parents 69b6c59 + 2ab1ca2 commit 951bef4

File tree

2 files changed

+22
-30
lines changed

2 files changed

+22
-30
lines changed

core/os/os2/env_linux.odin

+21-29
Original file line numberDiff line numberDiff line change
@@ -20,19 +20,18 @@ NOT_FOUND :: -1
2020
// the environment is a 0 delimited list of <key>=<value> strings
2121
_env: [dynamic]string
2222

23-
_env_mutex: sync.Mutex
23+
_env_mutex: sync.Recursive_Mutex
2424

2525
// We need to be able to figure out if the environment variable
2626
// is contained in the original environment or not. This also
2727
// serves as a flag to determine if we have built _env.
28-
_org_env_begin: uintptr
29-
_org_env_end: uintptr
28+
_org_env_begin: uintptr // atomic
29+
_org_env_end: uintptr // guarded by _env_mutex
3030

3131
// Returns value + index location into _env
3232
// or -1 if not found
3333
_lookup :: proc(key: string) -> (value: string, idx: int) {
34-
sync.mutex_lock(&_env_mutex)
35-
defer sync.mutex_unlock(&_env_mutex)
34+
sync.guard(&_env_mutex)
3635

3736
for entry, i in _env {
3837
if k, v := _kv_from_entry(entry); k == key {
@@ -43,7 +42,7 @@ _lookup :: proc(key: string) -> (value: string, idx: int) {
4342
}
4443

4544
_lookup_env :: proc(key: string, allocator: runtime.Allocator) -> (value: string, found: bool) {
46-
if _org_env_begin == 0 {
45+
if intrinsics.atomic_load_explicit(&_org_env_begin, .Acquire) == 0 {
4746
_build_env()
4847
}
4948

@@ -55,18 +54,17 @@ _lookup_env :: proc(key: string, allocator: runtime.Allocator) -> (value: string
5554
}
5655

5756
_set_env :: proc(key, v_new: string) -> Error {
58-
if _org_env_begin == 0 {
57+
if intrinsics.atomic_load_explicit(&_org_env_begin, .Acquire) == 0 {
5958
_build_env()
6059
}
60+
sync.guard(&_env_mutex)
6161

6262
// all key values are stored as "key=value\x00"
6363
kv_size := len(key) + len(v_new) + 2
6464
if v_curr, idx := _lookup(key); idx != NOT_FOUND {
6565
if v_curr == v_new {
6666
return nil
6767
}
68-
sync.mutex_lock(&_env_mutex)
69-
defer sync.mutex_unlock(&_env_mutex)
7068

7169
unordered_remove(&_env, idx)
7270

@@ -101,41 +99,37 @@ _set_env :: proc(key, v_new: string) -> Error {
10199
intrinsics.mem_copy_non_overlapping(&val_slice[0], raw_data(v_new), len(v_new))
102100
val_slice[len(v_new)] = 0
103101

104-
sync.mutex_lock(&_env_mutex)
105102
append(&_env, string(k_addr[:kv_size - 1]))
106-
sync.mutex_unlock(&_env_mutex)
107103
return nil
108104
}
109105

110106
_unset_env :: proc(key: string) -> bool {
111-
if _org_env_begin == 0 {
107+
if intrinsics.atomic_load_explicit(&_org_env_begin, .Acquire) == 0 {
112108
_build_env()
113109
}
110+
sync.guard(&_env_mutex)
114111

115112
v: string
116113
i: int
117114
if v, i = _lookup(key); i == -1 {
118115
return false
119116
}
120117

121-
sync.mutex_lock(&_env_mutex)
122118
unordered_remove(&_env, i)
123-
sync.mutex_unlock(&_env_mutex)
124119

125120
if _is_in_org_env(v) {
126121
return true
127122
}
128123

129-
// if we got this far, the envrionment variable
124+
// if we got this far, the environment variable
130125
// existed AND was allocated by us.
131126
k_addr, _ := _kv_addr_from_val(v, key)
132127
runtime.heap_free(k_addr)
133128
return true
134129
}
135130

136131
_clear_env :: proc() {
137-
sync.mutex_lock(&_env_mutex)
138-
defer sync.mutex_unlock(&_env_mutex)
132+
sync.guard(&_env_mutex)
139133

140134
for kv in _env {
141135
if !_is_in_org_env(kv) {
@@ -145,14 +139,16 @@ _clear_env :: proc() {
145139
clear(&_env)
146140

147141
// nothing resides in the original environment either
148-
_org_env_begin = ~uintptr(0)
142+
intrinsics.atomic_store_explicit(&_org_env_begin, ~uintptr(0), .Release)
149143
_org_env_end = ~uintptr(0)
150144
}
151145

152146
_environ :: proc(allocator: runtime.Allocator) -> (environ: []string, err: Error) {
153-
if _org_env_begin == 0 {
147+
if intrinsics.atomic_load_explicit(&_org_env_begin, .Acquire) == 0 {
154148
_build_env()
155149
}
150+
sync.guard(&_env_mutex)
151+
156152
env := make([dynamic]string, 0, len(_env), allocator) or_return
157153
defer if err != nil {
158154
for e in env {
@@ -161,8 +157,6 @@ _environ :: proc(allocator: runtime.Allocator) -> (environ: []string, err: Error
161157
delete(env)
162158
}
163159

164-
sync.mutex_lock(&_env_mutex)
165-
defer sync.mutex_unlock(&_env_mutex)
166160
for entry in _env {
167161
s := clone_string(entry, allocator) or_return
168162
append(&env, s)
@@ -174,36 +168,34 @@ _environ :: proc(allocator: runtime.Allocator) -> (environ: []string, err: Error
174168
// The entire environment is stored as 0 terminated strings,
175169
// so there is no need to clone/free individual variables
176170
export_cstring_environment :: proc(allocator: runtime.Allocator) -> []cstring {
177-
if _org_env_begin == 0 {
171+
if intrinsics.atomic_load_explicit(&_org_env_begin, .Acquire) == 0 {
178172
// The environment has not been modified, so we can just
179173
// send the original environment
180174
org_env := _get_original_env()
181175
n: int
182176
for ; org_env[n] != nil; n += 1 {}
183177
return slice.clone(org_env[:n + 1], allocator)
184178
}
179+
sync.guard(&_env_mutex)
185180

186181
// NOTE: already terminated by nil pointer via + 1
187182
env := make([]cstring, len(_env) + 1, allocator)
188183

189-
sync.mutex_lock(&_env_mutex)
190-
defer sync.mutex_unlock(&_env_mutex)
191184
for entry, i in _env {
192185
env[i] = cstring(raw_data(entry))
193186
}
194187
return env
195188
}
196189

197190
_build_env :: proc() {
198-
sync.mutex_lock(&_env_mutex)
199-
defer sync.mutex_unlock(&_env_mutex)
200-
if _org_env_begin != 0 {
191+
sync.guard(&_env_mutex)
192+
if intrinsics.atomic_load_explicit(&_org_env_begin, .Acquire) != 0 {
201193
return
202194
}
203195

204196
_env = make(type_of(_env), runtime.heap_allocator())
205197
cstring_env := _get_original_env()
206-
_org_env_begin = uintptr(rawptr(cstring_env[0]))
198+
intrinsics.atomic_store_explicit(&_org_env_begin, uintptr(rawptr(cstring_env[0])), .Release)
207199
for i := 0; cstring_env[i] != nil; i += 1 {
208200
bytes := ([^]u8)(cstring_env[i])
209201
n := len(cstring_env[i])
@@ -235,5 +227,5 @@ _kv_addr_from_val :: #force_inline proc(val: string, key: string) -> ([^]u8, [^]
235227

236228
_is_in_org_env :: #force_inline proc(env_data: string) -> bool {
237229
addr := uintptr(raw_data(env_data))
238-
return addr >= _org_env_begin && addr < _org_env_end
230+
return addr >= intrinsics.atomic_load_explicit(&_org_env_begin, .Acquire) && addr < _org_env_end
239231
}

core/os/os2/env_posix.odin

+1-1
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ _clear_env :: proc() {
5757
}
5858

5959
_environ :: proc(allocator: runtime.Allocator) -> (environ: []string, err: Error) {
60-
n := 0
60+
n := 0
6161
for entry := posix.environ[0]; entry != nil; n, entry = n+1, posix.environ[n] {}
6262

6363
r := make([dynamic]string, 0, n, allocator) or_return

0 commit comments

Comments
 (0)