1/*
2    Title:      Validate addresses in objects.
3
4    Copyright (c) 2006, 2012, 2017
5        David C.J. Matthews
6
7    This library is free software; you can redistribute it and/or
8    modify it under the terms of the GNU Lesser General Public
9    License as published by the Free Software Foundation; either
10    version 2.1 of the License, or (at your option) any later version.
11
12    This library is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15    Lesser General Public License for more details.
16
17    You should have received a copy of the GNU Lesser General Public
18    License along with this library; if not, write to the Free Software
19    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20
21*/
22#ifdef HAVE_CONFIG_H
23#include "config.h"
24#elif defined(_WIN32)
25#include "winconfig.h"
26#else
27#error "No configuration file"
28#endif
29
30#ifdef HAVE_ASSERT_H
31#include <assert.h>
32#define ASSERT(x)   assert(x)
33#else
34#define ASSERT(x)
35#endif
36
37
38#include "globals.h"
39#include "diagnostics.h"
40#include "machine_dep.h"
41#include "scanaddrs.h"
42#include "memmgr.h"
43
44#define INRANGE(val,start,end)\
45  (start <= val && val < end)
46
47static void CheckAddress(PolyWord *pt)
48{
49    MemSpace *space = gMem.SpaceForAddress(pt-1);
50    if (space == 0)
51    {
52        Log("Check: Bad pointer %p (no space found)\n", pt);
53        ASSERT(space != 0);
54    }
55    if (space->spaceType == ST_STACK) // This may not have valid length words.
56        return;
57    PolyObject *obj = (PolyObject*)pt;
58    ASSERT(obj->ContainsNormalLengthWord());
59    POLYUNSIGNED length = obj->Length();
60    if (pt+length > space->top)
61    {
62        Log("Check: Bad pointer %p (space %p) length %" POLYUFMT "\n", pt, space, length);
63        ASSERT(pt+length <= space->top);
64    }
65    if (space->spaceType == ST_LOCAL)
66    {
67        LocalMemSpace *lSpace = (LocalMemSpace*)space;
68        if (!((pt > lSpace->bottom && pt+length <= lSpace->lowerAllocPtr) ||
69            (pt > lSpace->upperAllocPtr && pt+length <= space->top)))
70        {
71            Log("Check: Bad pointer %p (space %p) length %" POLYUFMT " outside allocated area\n", pt, space, length);
72            ASSERT((pt > lSpace->bottom && pt+length <= lSpace->lowerAllocPtr) ||
73                (pt > lSpace->upperAllocPtr && pt+length <= space->top));
74        }
75    }
76}
77
78void DoCheck (const PolyWord pt)
79{
80    if (pt == PolyWord::FromUnsigned(0)) return;
81
82    if (pt.IsTagged()) return;
83
84    CheckAddress(pt.AsStackAddr());
85}
86
87class ScanCheckAddress: public ScanAddress
88{
89public:
90    virtual PolyObject *ScanObjectAddress(PolyObject *pt) { CheckAddress((PolyWord*)pt); return pt; }
91};
92
93void DoCheckObject (const PolyObject *base, POLYUNSIGNED L)
94{
95
96    PolyWord *pt  = (PolyWord*)base;
97    CheckAddress(pt);
98    MemSpace *space = gMem.SpaceForAddress(pt-1);
99    if (space == 0)
100        Crash ("Bad pointer 0x%08" PRIxPTR " found", (uintptr_t)pt);
101
102    ASSERT (OBJ_IS_LENGTH(L));
103
104    POLYUNSIGNED n   = OBJ_OBJECT_LENGTH(L);
105    if (n == 0) return;
106
107    ASSERT (n > 0);
108    ASSERT(pt-1 >= space->bottom && pt+n <= space->top);
109
110    byte flags = GetTypeBits(L);  /* discards GC flag and mutable bit */
111
112    if (flags == F_BYTE_OBJ) /* possibly signed byte object */
113        return; /* Nothing more to do */
114
115    if (flags == F_CODE_OBJ) /* code object */
116    {
117        ScanCheckAddress checkAddr;
118        /* We flush the instruction cache here in case we change any of the
119          instructions when we update addresses. */
120        machineDependent->FlushInstructionCache(pt, (n + 1) * sizeof(PolyWord));
121        machineDependent->ScanConstantsWithinCode((PolyObject *)base, (PolyObject *)base, n, &checkAddr);
122        /* Skip to the constants. */
123        base->GetConstSegmentForCode(n, pt, n);
124    }
125    else if (flags == F_CLOSURE_OBJ)
126    {
127        n -= sizeof(PolyObject*) / sizeof(PolyWord);
128        pt += sizeof(PolyObject*) / sizeof(PolyWord);
129    }
130    else ASSERT (flags == 0); /* ordinary word object */
131
132    while (n--) DoCheck (*pt++);
133}
134
135void DoCheckPointer (const PolyWord pt)
136{
137    if (pt == PolyWord::FromUnsigned(0)) return;
138
139    if (OBJ_IS_AN_INTEGER(pt)) return;
140
141    DoCheck (pt);
142
143    if (pt.IsDataPtr())
144    {
145        PolyObject *obj = pt.AsObjPtr();
146        DoCheckObject (obj, obj->LengthWord());
147    }
148}
149
150// Check all the objects in the memory.  Used to check the garbage collector
151//
152void DoCheckMemory()
153{
154    ScanCheckAddress memCheck;
155    // Scan the local areas.
156    for (std::vector<LocalMemSpace*>::iterator i = gMem.lSpaces.begin(); i < gMem.lSpaces.end(); i++)
157    {
158        LocalMemSpace *space = *i;
159        memCheck.ScanAddressesInRegion(space->bottom, space->lowerAllocPtr);
160        memCheck.ScanAddressesInRegion(space->upperAllocPtr, space->top);
161    }
162    // Scan the permanent mutable areas.
163    for (std::vector<PermanentMemSpace*>::iterator i = gMem.pSpaces.begin(); i < gMem.pSpaces.end(); i++)
164    {
165        PermanentMemSpace *space = *i;
166        if (space->isMutable && ! space->byteOnly)
167            memCheck.ScanAddressesInRegion(space->bottom, space->top);
168    }
169}
170