-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Description
ptr charimplicitly converts to cstring, resulting in undefined behavior- ditto with
ptr array[N, char]andptr UncheckedArray[N, char]
this introduces a weird special case in the language (eg$adoesn't compile for other types a: ptr[T] except for T=char) as well as weird bugs and undefined behavior in seemingly safe code
Example 1
when true:
import std/unittest
var x: ptr char
var s: cstring
doAssert cstring isnot ptr char
doAssert (ptr char) isnot cstring
proc fun(a: cstring)=discard
proc fun2(a: ptr char)=discard
doAssert not compiles(fun2 cstring"asdf")
doAssert not compiles(block: x = s)
# BUG
check not compiles(fun(x))
check not compiles(echo(x))
check not compiles($(x))
check not compiles(block: s = x)Current Output
/Users/timothee/git_clone/nim/timn/tests/nim/all/t10433.nim(15, 8): Check failed: not compiles(fun(x))
compiles(fun(x)) was true
/Users/timothee/git_clone/nim/timn/tests/nim/all/t10433.nim(16, 8): Check failed: not compiles(echo(x))
compiles(echo(x)) was true
/Users/timothee/git_clone/nim/timn/tests/nim/all/t10433.nim(17, 8): Check failed: not compiles($(x))
compiles($(x)) was true
Expected Output
tests pass
ie, it should be illegal to do those implicit conversions from ptr char to cstring
Example 2
in case you may think it's a good idea to have implicit conversion from ptr char to cstring, consider this:
when true:
proc fun(a, b: var openArray[char]) =
echo a[0].addr # prints abcdef sometimes, or sometimes other garbage
proc main()=
var a = ['a','b', 'c']
var b = ['d','e', 'f']
fun(a,b)
main()you get (different) garbage results when recompiling the same program:
nim c -r $timn_D/tests/nim/all/t10433.nim
abc`UG
nim c -r $timn_D/tests/nim/all/t10433.nim
abc`%
Example 3
in other cases, you'll end up with buffer overflow bugs/attacks, since a ptr char could come from any source (eg ffi calls, malloc / createU, etc) with nothing that should guarantee that it's 0 terminated.
That problem is exacerbated when a ptr char field is nested somewhere deep inside an object and we are calling $ on it
Example 4
likewise, implicit conversion from ptr array[0..2, char] to cstring should be disallowed. It has the same caveats, eg:
when true:
var vals = @[['a','b','c'], ['d','e','f']]
echo vals[0]
echo vals[0].addr # should not compile (unless `$` has an overload for ptr)
var z: cstring = vals[0].addr # should not compileprints
['a', 'b', 'c']
abcdef # error prone!
and ditto for ptr UncheckedArray, eg:
when true:
var vals1 = @[['a','b','c'], ['d','e','f']]
echo cast[ptr UncheckedArray[char]](vals1[0].addr) # prints
Possible Solution
- prevent implicit conversion (sigmatch) from X to cstring where X is not cstring (eg
ptr char,ptr[array[N, char]etc) - add an easy way to convert explicitly, eg:
template toCstring*(a: ptr char): cstring = cast[cstring](a)- if the worry is about backward compatibility, then make it an error but allow a flag:
--legacy:implicitPtrCharToCstring(adding to existing list--legacy:allowSemcheckedAstModification|checkUnsignedConversions)
Additional Information
- latest devel 0a49fe5 1.1.1
- since at least 0.17 or forever
- D20200327T225837