1 // PERMUTE_ARGS:
2 // REQUIRED_ARGS:
3 
4 import core.memory;
5 
6 // see http://forum.dlang.org/thread/4BB6296E.6050506@digitalmars.com for more info
7 
8 // failure case for bug fixed by druntime rev 282
9 // how it works:
10 // The erroneous code used the first element in the LRU cache to determine the
11 // length of the block, instead of looking at the current blocks blockinfo.  So
12 // we need a somewhat alignment of moons to get this to fail.  First, we create
13 // the perfect storm: 1) a long blockinfo at the beginning (size = 32), 2) the
14 // second blockinfo is smaller (16 bytes) and 3) a block of memory that is
15 // exactly 16 bytes past the element in the 2nd block.  While this seems kind
16 // of unlikely, in code that uses a lot of appending, it can easily happen.
17 //
18 // The bug causes the array appender to think that a pointer in the 16 bytes
19 // past the second blockinfo is actually in the second blockinfo.  This is
20 // because it uses the length from the first blockinfo for checking (which is
21 // 32, so since the pointer is only 16 bytes into the block, it is considered
22 // inside).
23 // On top of all this, the third block must be marked as containing pointers,
24 // and the second block not containing pointers.  The reason is because, it's
25 // impossible for the runtime to append in place, since the outside pointer
26 // can't possibly match the array length in the block.  But because the runtime
27 // copies the blockinfo's attributes when reallocating, it copies the
28 // attributes from the *wrong* block.  The only way to make this cause a
29 // problem would be to then collect memory, which should incorrectly deallocate
30 // data that the array containing pointers points to, and then use those
31 // pointers.  However, for our purposes, we know this is possible, but we can
32 // deterministically check the attributes of the array after appending and
33 // verify that they are wrong.
34 //
main()35 void main()
36 {
37     // fill up the cache to make it wrap, The block info cache has 8 elements,
38     // and the first element is not the first one filled, so we want to wrap to
39     // that first element we want to fill in
40     for(int i = 0; i < 7; i++)
41     {
42         auto n = new int[1];
43         n ~= 1;
44     }
45 
46     // this will allocate a 32-byte block, and appending will insert it into
47     // the cache at the beginning.
48     auto y = new int[4];
49     y ~= 1;
50 
51     // now, allocate a 16 byte block with pointers, this will be our block that
52     // gets corrupted.  The current GC allocates down, so we allocate this one
53     // first.
54     auto x = new char[][1];
55 
56     // this block contains no pointers, and appending will insert it into the
57     // second element of the cache
58     y = new int[1];
59     y ~= 1;
60 
61     // verify the noscan flag is 0 on the pointer-containing blocks
62     assert((GC.getAttr(x.ptr) & GC.BlkAttr.NO_SCAN) == 0);
63     x ~= "hello".dup; // this should leave the attributes alone
64     assert((GC.getAttr(x.ptr) & GC.BlkAttr.NO_SCAN) == 0); // fails on 2.042
65 }
66