c3d / db48x

RPL runtime for the DM42 calculator, in the spirit of HP48/49/50
http://48calc.org
GNU Lesser General Public License v3.0
115 stars 15 forks source link

Memory corruption, presumably from grob bitmaps #1115

Closed c3d closed 3 months ago

c3d commented 3 months ago

Running the test suite, occasional crash / assert solving equations:

 37: 001   0.0:  Enter directory for solving                             [PASS]
 37: 002   0.0:  Solving Elastic Buckling                                [PASS]
 37: 003   0.5:  Solving Elastic Buckling second equation                [PASS]
 37: 004   0.6:  Solving Elastic Buckling third equation                 [PASS]
 37: 005   0.8:  Solving Elastic Buckling fourth equation                [PASS]
 37: 006   0.9:  Solving Eccentric Columns                               [PASS]
 37: 007   1.8:  Solving Eccentric Column second equation                [PASS]
 37: 008   1.9:  Solving Simple Deflection

Breakpoint in object_error shows that the problem is in graphic rendering:

frame #4: 0x00000001000a6ecc db48x`expression::graph(grapher&, unsigned int, int&) + 4420 at /Users/ddd/Work/calc/db48x/src/expression.cc:1936
   1933                 g.font      = fid;
   1934             grob_g lg   = graph(g, depth, lprec);
   1935             coord  lv   = g.voffset;
-> 1936             int    prec = obj->precedence();
                                       ^
   1937             g.font = fid;
   1938             if (prec == precedence::FUNCTION &&
   1939                 oid != ID_xroot && oid != ID_comb && oid != ID_perm)

The object is a bit after the graphic object, and that its content has been overwritten by what looks like bitap data:

(lldb) p obj
p obj
(object_g) {
  runtime::gcptr = {
    safe = 0x000000010f22ebbc "\xff\xf9\xff\xf9\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf3\xff\xf3\xff\xfc3\xfe\x80\xcf\xc0?\xf0\U0000001f\xf8\U0000000f\xfc\a\xff\xff\xf3\xff\xf0\U0000001f\xf8\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe1?\xf0\U0000000f\xfc\a\xff\x80ρ?\xf0?\xfc\U0000001f\xf8\x8f\xff\xff\xf3\xff\xe1?\xf0\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xc3\U0000007f\xe0\U0000001f\xf8\x8f\xff\x80\x9f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf9\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x

The object was valid before the invokation of graphing functoin, so this seems to confirm that there was some kind of drawing outside of bounds.

(lldb) p oid
p oid
(object::id) ID_sub
(lldb) d rg
d rg
(cstring) 0x000000010f22ec72 "Graphic 150 x 42"
(lldb) d lg
d lg
(cstring) 0x000000010f22ec72 "Graphic 305 x 52"
(lldb) x obj.safe
x obj.safe
0x10f22ebbc: ff f9 ff f9 ff ff ff ff ff ff ff ff ff ff ff ff  ................
0x10f22ebcc: ff ff ff f3 ff f3 ff fc 33 fe 80 cf c0 3f f0 1f  ........3....?..
c3d commented 3 months ago

The problem appears to be really nasty, since it seems like a live garbage collected pointer (obj) is corrupt and points to a place where it was overwritten by a newly created graphic object. There is no obvious trace of it in the flight recorder.

More specifically:

(lldb) p obj.safe
(byte *) 0x000000010f22ebbc "\xff\xf9\xff...
(lldb) p (305+7)/8*8
p (305+7)/8*8
(int) 312
(lldb) p 312*52
p 312*52
(int) 16224
(lldb) p 312*52/8
p 312*52/8
(int) 2028
(lldb) p lg->size()
p lg->size()
(size_t) 2032

(The four bytes overhead is: 1-byte for ID, 2-bytes for 305, one byte for 52)

The problem is that obj is pointing to a wrong spot, there are only 1850 bytes between obj and lg, less than the 2032 in lg.

(lldb) p obj.safe-lg.safe
p obj.safe-lg.safe
(long) 1850
c3d commented 3 months ago

lg is indeed the last object created:

p (byte*)rt.Temporaries-(byte*)lg.safe
(long) 2032

So it's really a matter of obj pointing to something wrong.

c3d commented 3 months ago

The current oid is ID_sub

(lldb) p oid
(object::id) ID_sub

One level up, it's ID_TestEQ:

(lldb) up
frame #5: 0x00000001000a6e24 db48x`expression::graph(grapher&, unsigned int, int&) + 4252 at /Users/ddd/Work/calc/db48x/src/expression.cc:1930
   1927             if (oid == ID_pow || oid == ID_xroot ||
   1928                 oid == ID_comb || oid == ID_perm)
   1929                 g.reduce_font();
-> 1930             grob_g rg   = graph(g, depth, rprec);
                                  ^
   1931             coord  rv   = g.voffset;
   1932             if (oid != ID_comb && oid != ID_perm)
   1933                 g.font      = fid;
(lldb) p oid
(object::id) ID_TestEQ

This is consistent with expectations. One level up, we are in GRAPH_BODY(expression), but expr is not an expression, it's an equation. That may be part of the problem.

(lldb) d expr
(cstring) 0x000000010f22ec72 "EquationsMenu14"
(lldb) p expr->type()
(object::id) ID_EquationsMenu14
c3d commented 3 months ago

Above that, we are in stack rendering code, so we are rendering an equation as a transient object through draw_object. This is expected. What we have here is where the problem most likely resides: draw_object iteratoes on graph = obj->graph(g) without having a GC pointer for obj. So that's how we got a stale obj down the chain.

c3d commented 3 months ago

That theory would explain why we only see that in the solving menu, which is practically the only place with a transient object being drawn.