Make the utf16 conversion procedures in core:sys/windows safer by checking for memory leaks

This commit is contained in:
gingerBill
2022-05-12 13:17:58 +01:00
parent bb4f108487
commit eef44b11f3
17 changed files with 105 additions and 72 deletions
+1 -1
View File
@@ -126,7 +126,7 @@ _open_file_dialog :: proc(title: string, dir: string,
}
file_name := utf16_to_utf8(file_buf[:], allocator)
file_name, _ := utf16_to_utf8(file_buf[:], allocator)
path = strings.trim_right_null(file_name)
return
}
+1 -1
View File
@@ -10,6 +10,6 @@ foreign {
get_cwd :: proc(allocator := context.temp_allocator) -> string {
buffer := make([]u16, MAX_PATH_WIDE, allocator)
_get_cwd_wide(Wstring(&buffer[0]), MAX_PATH_WIDE)
file := utf16_to_utf8(buffer[:], allocator)
file, _ := utf16_to_utf8(buffer[:], allocator)
return strings.trim_right_null(file)
}
+10 -8
View File
@@ -1,6 +1,8 @@
// +build windows
package win32
import "core:runtime"
Uint_Ptr :: distinct uintptr
Int_Ptr :: distinct int
Long_Ptr :: distinct int
@@ -858,14 +860,14 @@ utf8_to_wstring :: proc(s: string, allocator := context.temp_allocator) -> Wstri
return nil
}
wstring_to_utf8 :: proc(s: Wstring, N: int, allocator := context.temp_allocator) -> string {
wstring_to_utf8 :: proc(s: Wstring, N: int, allocator := context.temp_allocator) -> (str: string, err: runtime.Allocator_Error) {
if N == 0 {
return ""
return
}
n := wide_char_to_multi_byte(CP_UTF8, WC_ERR_INVALID_CHARS, s, i32(N), nil, 0, nil, nil)
if n == 0 {
return ""
return
}
// If N == -1 the call to wide_char_to_multi_byte assume the wide string is null terminated
@@ -873,11 +875,11 @@ wstring_to_utf8 :: proc(s: Wstring, N: int, allocator := context.temp_allocator)
// also null terminated.
// If N != -1 it assumes the wide string is not null terminated and the resulting string
// will not be null terminated, we therefore have to force it to be null terminated manually.
text := make([]byte, n+1 if N != -1 else n, allocator)
text := make([]byte, n+1 if N != -1 else n, allocator) or_return
if n1 := wide_char_to_multi_byte(CP_UTF8, WC_ERR_INVALID_CHARS, s, i32(N), cstring(&text[0]), n, nil, nil); n1 == 0 {
delete(text, allocator)
return ""
return "", nil
}
for i in 0..<n {
@@ -887,12 +889,12 @@ wstring_to_utf8 :: proc(s: Wstring, N: int, allocator := context.temp_allocator)
}
}
return string(text[:n])
return string(text[:n]), nil
}
utf16_to_utf8 :: proc(s: []u16, allocator := context.temp_allocator) -> string {
utf16_to_utf8 :: proc(s: []u16, allocator := context.temp_allocator) -> (string, runtime.Allocator_Error) {
if len(s) == 0 {
return ""
return "", nil
}
return wstring_to_utf8(cast(Wstring)&s[0], len(s), allocator)
}
+23 -23
View File
@@ -1,41 +1,41 @@
package win32_tests
import "core:sys/win32"
import win32 "core:sys/windows"
import "core:testing"
utf16_to_utf8 :: proc(t: ^testing.T, str: []u16, comparison: string, expected_result: bool, loc := #caller_location) {
result := win32.utf16_to_utf8(str[:]);
testing.expect(t, (result == comparison) == expected_result, "Incorrect utf16_to_utf8 conversion", loc);
result, _ := win32.utf16_to_utf8(str[:])
testing.expect(t, (result == comparison) == expected_result, "Incorrect utf16_to_utf8 conversion", loc)
}
wstring_to_utf8 :: proc(t: ^testing.T, str: []u16, comparison: string, expected_result: bool, loc := #caller_location) {
result := win32.wstring_to_utf8(nil if len(str) == 0 else cast(win32.Wstring)&str[0], -1);
testing.expect(t, (result == comparison) == expected_result, "Incorrect wstring_to_utf8 conversion", loc);
result, _ := win32.wstring_to_utf8(nil if len(str) == 0 else cast(win32.Wstring)&str[0], -1)
testing.expect(t, (result == comparison) == expected_result, "Incorrect wstring_to_utf8 conversion", loc)
}
@test
test_utf :: proc(t: ^testing.T) {
utf16_to_utf8(t, []u16{}, "", true);
utf16_to_utf8(t, []u16{0}, "", true);
utf16_to_utf8(t, []u16{0, 't', 'e', 's', 't'}, "", true);
utf16_to_utf8(t, []u16{0, 't', 'e', 's', 't', 0}, "", true);
utf16_to_utf8(t, []u16{'t', 'e', 's', 't'}, "test", true);
utf16_to_utf8(t, []u16{'t', 'e', 's', 't', 0}, "test", true);
utf16_to_utf8(t, []u16{'t', 'e', 0, 's', 't'}, "te", true);
utf16_to_utf8(t, []u16{'t', 'e', 0, 's', 't', 0}, "te", true);
utf16_to_utf8(t, []u16{}, "", true)
utf16_to_utf8(t, []u16{0}, "", true)
utf16_to_utf8(t, []u16{0, 't', 'e', 's', 't'}, "", true)
utf16_to_utf8(t, []u16{0, 't', 'e', 's', 't', 0}, "", true)
utf16_to_utf8(t, []u16{'t', 'e', 's', 't'}, "test", true)
utf16_to_utf8(t, []u16{'t', 'e', 's', 't', 0}, "test", true)
utf16_to_utf8(t, []u16{'t', 'e', 0, 's', 't'}, "te", true)
utf16_to_utf8(t, []u16{'t', 'e', 0, 's', 't', 0}, "te", true)
wstring_to_utf8(t, []u16{}, "", true);
wstring_to_utf8(t, []u16{0}, "", true);
wstring_to_utf8(t, []u16{0, 't', 'e', 's', 't'}, "", true);
wstring_to_utf8(t, []u16{0, 't', 'e', 's', 't', 0}, "", true);
wstring_to_utf8(t, []u16{'t', 'e', 's', 't', 0}, "test", true);
wstring_to_utf8(t, []u16{'t', 'e', 0, 's', 't'}, "te", true);
wstring_to_utf8(t, []u16{'t', 'e', 0, 's', 't', 0}, "te", true);
wstring_to_utf8(t, []u16{}, "", true)
wstring_to_utf8(t, []u16{0}, "", true)
wstring_to_utf8(t, []u16{0, 't', 'e', 's', 't'}, "", true)
wstring_to_utf8(t, []u16{0, 't', 'e', 's', 't', 0}, "", true)
wstring_to_utf8(t, []u16{'t', 'e', 's', 't', 0}, "test", true)
wstring_to_utf8(t, []u16{'t', 'e', 0, 's', 't'}, "te", true)
wstring_to_utf8(t, []u16{'t', 'e', 0, 's', 't', 0}, "te", true)
// WARNING: Passing a non-zero-terminated string to wstring_to_utf8 is dangerous,
// as it will go out of bounds looking for a zero.
// It will "fail" or "succeed" by having a zero just after the end of the input string or not.
wstring_to_utf8(t, []u16{'t', 'e', 's', 't'}, "test", false);
wstring_to_utf8(t, []u16{'t', 'e', 's', 't', 0}[:4], "test", true);
wstring_to_utf8(t, []u16{'t', 'e', 's', 't', 'q'}[:4], "test", false);
wstring_to_utf8(t, []u16{'t', 'e', 's', 't'}, "test", false)
wstring_to_utf8(t, []u16{'t', 'e', 's', 't', 0}[:4], "test", true)
wstring_to_utf8(t, []u16{'t', 'e', 's', 't', 'q'}[:4], "test", false)
}
+5
View File
@@ -272,6 +272,11 @@ foreign kernel32 {
HeapReAlloc :: proc(hHeap: HANDLE, dwFlags: DWORD, lpMem: LPVOID, dwBytes: SIZE_T) -> LPVOID ---
HeapFree :: proc(hHeap: HANDLE, dwFlags: DWORD, lpMem: LPVOID) -> BOOL ---
LocalAlloc :: proc(flags: UINT, bytes: SIZE_T) -> LPVOID ---
LocalReAlloc :: proc(mem: LPVOID, bytes: SIZE_T, flags: UINT) -> LPVOID ---
LocalFree :: proc(mem: LPVOID) -> LPVOID ---
ReadDirectoryChangesW :: proc(
hDirectory: HANDLE,
lpBuffer: LPVOID,
+13 -13
View File
@@ -2,7 +2,7 @@
package sys_windows
import "core:strings"
import "core:sys/win32"
import "core:runtime"
import "core:intrinsics"
L :: intrinsics.constant_utf16_cstring
@@ -56,16 +56,16 @@ utf8_to_wstring :: proc(s: string, allocator := context.temp_allocator) -> wstri
return nil
}
wstring_to_utf8 :: proc(s: wstring, N: int, allocator := context.temp_allocator) -> (res: string) {
wstring_to_utf8 :: proc(s: wstring, N: int, allocator := context.temp_allocator) -> (res: string, err: runtime.Allocator_Error) {
context.allocator = allocator
if N <= 0 {
return ""
return
}
n := WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, s, i32(N), nil, 0, nil, nil)
if n == 0 {
return ""
return
}
// If N == -1 the call to WideCharToMultiByte assume the wide string is null terminated
@@ -73,12 +73,12 @@ wstring_to_utf8 :: proc(s: wstring, N: int, allocator := context.temp_allocator)
// also null terminated.
// If N != -1 it assumes the wide string is not null terminated and the resulting string
// will not be null terminated, we therefore have to force it to be null terminated manually.
text := make([]byte, n+1 if N != -1 else n)
text := make([]byte, n+1 if N != -1 else n) or_return
n1 := WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, s, i32(N), raw_data(text), n, nil, nil)
if n1 == 0 {
delete(text, allocator)
return ""
return
}
for i in 0..<n {
@@ -87,12 +87,12 @@ wstring_to_utf8 :: proc(s: wstring, N: int, allocator := context.temp_allocator)
break
}
}
return string(text[:n])
return string(text[:n]), nil
}
utf16_to_utf8 :: proc(s: []u16, allocator := context.temp_allocator) -> string {
utf16_to_utf8 :: proc(s: []u16, allocator := context.temp_allocator) -> (res: string, err: runtime.Allocator_Error) {
if len(s) == 0 {
return ""
return "", nil
}
return wstring_to_utf8(raw_data(s), len(s), allocator)
}
@@ -216,7 +216,7 @@ get_computer_name_and_account_sid :: proc(username: string) -> (computer_name: s
if !res {
return "", {}, false
}
computer_name = utf16_to_utf8(cname_w, context.temp_allocator)
computer_name = utf16_to_utf8(cname_w, context.temp_allocator) or_else ""
ok = true
return
@@ -306,7 +306,7 @@ add_user_profile :: proc(username: string) -> (ok: bool, profile_path: string) {
if res == false {
return false, ""
}
defer win32.local_free(sb)
defer LocalFree(sb)
pszProfilePath := make([]u16, 257, context.temp_allocator)
res2 := CreateProfile(
@@ -318,7 +318,7 @@ add_user_profile :: proc(username: string) -> (ok: bool, profile_path: string) {
if res2 != 0 {
return false, ""
}
profile_path = wstring_to_utf8(&pszProfilePath[0], 257)
profile_path = wstring_to_utf8(&pszProfilePath[0], 257) or_else ""
return true, profile_path
}
@@ -336,7 +336,7 @@ delete_user_profile :: proc(username: string) -> (ok: bool) {
if res == false {
return false
}
defer win32.local_free(sb)
defer LocalFree(sb)
res2 := DeleteProfileW(
sb,