Comments (11)
what happens is:
tmp = createCallback(10)
=sink(p,tmp)
p():
void* colonenvP_ = ClE_0;
tmp = createCallback(99)
=sink(p,tmp) #destroys p.env, replaces with new one.
echo colonenvP_ #use after free
from nim.
i've played with this a bit, in nim and in c++, and i think it's undefined behaviour. whether the output should be 10,99 or undefined depends on so many subtleties
by that i mean:
proc closure(self: Environment) =
# here we destroy the global p and replace with a 'new' one.
# including the environment. so, does 'self' point to the old env, or to the new one?
# or, since we've destroyed the old env, which is the same pointed to by 'self'
# is self now invalid, pointing to garbage?
# garbage which, depending on the allocator could be an old value?
fwiw it's possible to get 10,99 or garbage output with versions of this written in c++ godbolt
in versions written in nim
sometimes valgrind reports that the closure is accessing free'd memory
sometimes, running valgrind changes the output!
from nim.
In your example, assuming that Environment
is a reference counted, heap allocated object, everything would work fine though, as the reference count of self
is incremented upon entering closure
, which prevents a deallocation.
Edit: C++ behaves differently here, as (at least I assume that) its closures are value types.
from nim.
To further demonstrate why I think that this should not be UB, lets just manually increase the refcount of the closure by one upon entering the callback.
var p: proc()
proc createCallback(x: int): proc() =
result = proc() =
var r = p
for it in 0..<100:
p = createCallback(it)
echo x
echo r.repr
for it in 0..<10:
p = createCallback(10)
p()
Now everything works as expected. And valgrind does not report any errors anymore,
from nim.
Looks like an ARC/ORC related optimizer bug to me. What does --mm:orc --cursorInference:off
produce?
from nim.
i agree that the environment of p has to be manually increfed
you need to do that to make the c++ version pass valgrind as well.
std::shared_ptr<std::function<void()>> p;
...
return [x]{
auto r = p;...}
from nim.
-d:useMalloc --mm:arc --cursorInference:off
also produces 10,99... and valgrind warns of a use after free
from nim.
Slightly simplified
var p: proc()
proc createCallback(x: int): proc() =
proc bark() =
p = createCallback(0)
p = createCallback(1)
echo x
result = bark
proc foo =
p = createCallback(10)
p()
foo()
from nim.
Perhaps, we could keep a local copy of the envs in that function to ensure its aliveness until the exiting of the closure scopes like
var p: proc()
proc createCallback(x: int): proc() =
proc bark() =
let tmpEnv
`=copy`(tmpEnv, env) # keeps envs alive
p = createCallback(0)
p = createCallback(1)
echo tmpEnv.x
result = bark
proc foo =
p = createCallback(10)
p()
foo()
Though "$1 = ($2) ClE_0;$n"
is hardcoded in the backends, is there a good solution to clean it up?
Probably it's a good compromise to just inc/dec the rc of ClE_0
from nim.
I'm not sure of the good solution for this problem, but if you want to manually increase the rc
from system {.all.} import nimIncRef
proc incRef(x: proc () {.closure.}) =
if x != nil:
nimIncRef(rawEnv(x))
proc decRef(x: proc () {.closure.}) =
var y {.cursor.} = x
`=destroy`(y)
var p: proc()
proc createCallback(x: int): proc() =
result = proc() =
let tmp {.cursor.} = p
incRef(tmp)
for it in 0..<100:
p = createCallback(it)
echo x
decRef(tmp)
for it in 0..<10:
p = createCallback(10)
p()
from nim.
Here is what I think of is happening:
var p: proc()
proc createCallback(x: int): proc() =
proc bark() =
# ClE_0.rc = 1; ClE_0.x = 10
p = createCallback(0)
# `=sink` or `=copy` decRef(ClE_0)
# ClE_0.rc = 0; ClE_0.x = 10 => the memory is reclaimed; but not zeroed
p = createCallback(999)
# With default alloc: ClE_0 is corrupted by the newly created memory of `createCallback(1)`
# but malloc is not affected in this case
echo x
result = bark
proc foo =
p = createCallback(10)
p()
foo()
refc: 10
orc: 999
orc -d:useMalloc: 10
from nim.
Related Issues (20)
- Async `finally` swallows errors when it contains an `await` wrapped by `try/except`
- The `when nimvm` statement fails to compile when in a file called nimvm.nim HOT 4
- Regression from 2.0 to `devel` with `dirty` template HOT 3
- Async memory leak when raising exception on ORC HOT 1
- unicode.splitWhitespace() and strutils.splitWhitespace() have different results for ASCII string
- Wrong stack trace when exception is raised in template HOT 1
- Error: cannot evaluate at compile time: foo HOT 4
- nim check crashes
- Any differnece between value types and reference types for `=sink` ? HOT 1
- Simple destructor code gives invalid C HOT 4
- Compiler crashes with infinite recursion for nested generic instantiation with static[int]
- The stdout.write doesn't print to terminal until new line symbol is sent HOT 1
- Gc_ref(x: string) and Gc_ref[T](x: seq[T]) doesn't exists anymore ? HOT 2
- tasks.toTask Doesn't Expect a Dot Expression
- [potential bug] SIGILL at `renderer.atom()`
- SIGSEGV due to freeing of uninitialized local string, during exception handling HOT 21
- Parameterless Conversions and Upcasting Are Broken
- Thread var cannot be returned from a proc. Causing SIGSEV
- C++ generation issue when wrapping constructors
- C++ compilation fails with: 'T1_' was not declared in this scope
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from nim.