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