Log In  

The following program, despite never adding a function to this table, sometimes has a function in the table. This was happening in a game of mine and I finally decided to try reproducing it with a tiny program and I have been able to.

cls()
nds={flr(rnd(4))}
for i=1,1000 do
 for k=1,flr(rnd(10)) do
  add(nds,flr(rnd(5)))
 end
 for j=1,1000 do
     nds={flr(rnd(4))}
     for d in all(nds) do
      if type(d)=='function' then
       print("function")
      end
     end
 end
end

I would never expect this program to ever print "function" yet, it does, and intermittently.

P#64550 2019-05-18 14:21 ( Edited 2019-05-18 14:22)

Adding additional statements (such as printing the values of I and J) seems to alter the number of function collisions.

P#64555 2019-05-18 15:01 ( Edited 2019-05-18 15:02)
:: bab_B

We've been discussing this on the Discord since last night. The truly trippy thing about it is that it appears Pico-8 is behaving nondeterministically:

cls()
total = 0
for i=1,2000 do
    for j=1,2000 do
        srand(0xb2e6.0fa3)
        nds={flr(rnd(4))}
        for d in all(nds) do
            if type(d)=='function' then
                print("function")
                total+=1
            end
        end
        total+=0
    end
end
print(total)

Notice that in this version, the RNG seed is being reset every time inside the innermost loop. And yet, in 26 out of the 4,000,000 iterations, type(d)=='function'.

Further weirdness: see what happens when you remove the seemingly extraneous line "total+=0"

EDIT: Actually, you can remove the call to rnd() entirely (nds={flr(0)}) and flr will still return a function as its second return value in 14 out of 4,000,000 iterations. Spooky stuff here.

P#64556 2019-05-18 15:01 ( Edited 2019-05-18 15:27)

The following reproduces:

cls()
for i=1,1000 do
 for j=1,1000 do
     ndxs={rnd(1)}
     if (type(ndxs[2]) == 'function') then
        print("ohno")
     end
 end
end

The following does not:

cls()
for i=1,1000 do
 for j=1,1000 do
     ndxs={(rnd(1))}
     if (type(ndxs[2]) == 'function') then
        print("ohno")
     end
 end
end

Note the extra parens around the rnd(1).
These make it so that if rnd(1) returns multiple values, only the first one is taken.
This workaround works in the original case as well.

So looks like some functions return an extra function as a second return value? (Could be C code mishaps)

P#64557 2019-05-18 15:07 ( Edited 2019-05-18 15:12)
:: Felice

Yeah, this will be the native C routine telling the interpreter that it's returning two (or more) values, but only filling one in and letting the other one be uninitialized memory, probably.

P#64595 2019-05-19 23:32 ( Edited 2019-05-19 23:40)
:: Felice

I did some testing and you seem to get the extra function value back from rnd() once every other frame, no matter what the framerate is.

Probably doesn't matter, since the real fix is to make sure it only says it's returning one value.

By the way, the function being returned seems to be the main loop that wraps around our editable source. So, uh, @zep ... that seems very likely to be exploitable in bad ways. You really ought to patch this asap.

P#64600 2019-05-20 00:21 ( Edited 2019-05-20 00:23)
:: zep
1

Fixed for 0.1.12d. That was an adventure.

I was incorrectly pushing the program object to the Lua stack after skipping a frame due to going over cpu. You'd think that would be quite a noticeable mistake, but the extra stack item is (it seems!) normally consumed somewhere harmlessly, and the stack doesn't grow. But in this case, the program is being interrupted just after a function pushes its return values to the stack, and the extra stack item is tagging along for the ride.

This will also need a cart version bump to fix properly, but I think it's rare enough that I'll put it off until beta. (So, it will be fixed in 0.1.12d, but 0.1.12c users will still be able to load and run 0.1.12d carts and potentially experience the bug)

@Felice I couldn't see the danger in getting the function back, except for being able to send PICO-8 into an infinite loop -- a hard freeze, but no corruption or access to out-of-bounds memory as far as I can tell. Either way I'm planning to release 0.1.12d soon, but perhaps there is something more dangerous I'm missing?

P#64974 2019-06-03 18:18

Way to track it down. One of those rare occurrence bugs! We initially didn't believe it was really happening. :D :P

P#64975 2019-06-03 19:05

[Please log in to post a comment]

About | Contact | Updates | Terms of Use
Follow Lexaloffle:        
Generated 2019-10-22 15:21 | 0.021s | 4194k | Q:26