![]() |
![]() HP OpenVMS Systemsask the wizard |
![]() |
The Question is: I have written a C program using DEC C version 6.0. In this C program I make a call to getenv. The pointer that returns from the getenv overrites a memory area that I have previously allocated using malloc. Am I doing something wrong here? This progra m has run for years on a VAX machine using DEC C version 4.0. Thanks The Answer is : Your program would initially appear to contain what is often refered to as a programming bug -- that a particular program has run for years only indicates that there are no overt problems. (OpenVMS Engineering has recently identified a bug that has been latent for over twenty years.) Problems in the memory allocation and deallocation routines provided by OpenVMS are certainly possible, though the level of use that these routines generally see also makes these bugs a rather rare species. The OpenVMS Wizard would initially suspect that a previous memory allocation is underrunning or overrunning the allocated storage, or that the application is writing to memory that has been deallocated. Various classes of stack errors can also arise, where too much or too little information is written to the variables stored on the stack, causing corruptions of other nearby variables. Alternatives include various asynchronous writes, particularly where access is unsynchronized or where the target variable is allocated in an inactive scope. The C malloc and free calls are very heavily used within OpenVMS code and within layered products and customer application code. Accordingly, any problems that might be lurking within the run-time library (RTL) code that implements these calls would typically be expected to have very obvious and very widespread effects. The Compaq C RTL ships with OpenVMS and not the compiler, and OpenVMS ECO kits for the RTL are available. That said, the OpenVMS Wizard would NOT assume that an ECO will repair a memory management problem. The OpenVMS Wizard WOULD assume that there is an application bug here. Additionally, use of current compilers is also recommended, but this again is far from a certain way to repair a memory management problem. The following is a typical scheme for managing a pool of variable-sized memory, and is similar to what is used within various of the OpenVMS system memory pools. The fixed overhead is eight bytes assuming longword (32-bit) addressing and sixteen bytes assuming quadword (64-bit) addressing, and the variable overhead -- assuming quadword alignment, which is a common assumption -- is somewhere between zero and seven bytes. +--------+--------+--------+--------+ | forward pointer (offset or abs) |\ +--------+--------+--------+--------+ > fixed overhead | size of this block in bytes |/ *========*========*========*========* # userdata (assume 9 bytes) #\ * * \ # # > variable size user data *========*========*========* * / | wasted space # #/ + *========* | |> wasted_bytes +--------+--------+--------+--------+ average bytes wasted: ((sizeof( alignment ) - 1) / 2) + sizeof(fixed overhead) For simplicity of the allocation (and, given the performance benefits of natural alignment), the size of the fixed overhead area is typically the same as the alignment specified for the user data area. Since the size of the offsets and sizes involved are typically powers of 2, the waste at the end of each chunk of pool is likely less than wasted_bytes, on average. Of course, any code that cares about the alignment of the buffers or about the pointer organization within the areas ajoining the memory returned to the caller -- or cares directly or indirectly about the wasted bytes -- can be suspect when porting the code to other platforms. For an example of code that indirectly cares about the wasted bytes, consider code that (erroneously) writes data into the wasted space. So long as there is sufficient extra space "wasted" in the allocation, the code will operate and the application will have a latent bug. Should the code be ported or should the allocation scheme change or -- the most common variation that exposes the bug -- should the allocation size change and allocate less (or no) wasted space -- the latent bug will be exposed. (And since the code "worked fine" on the other platform, the application code itself "clearly cannot be at fault".) The OpenVMS Wizard would certainly like to see ANSI C memory management extensions -- in particular, one that allows a user to specify the required alignment in pool, and that allows a programmer to define one or more fixed-size lookaside lists for performance. Extensions for debugging, such as the add-on tool "purify", would also be useful. (The OpenVMS C and LIBRTL run-time libraries do adapt to the allocation pattern of the application, with a selection of lookaside lists of fixed-size blocks and such. And OpenVMS does provide the Heap Analyzer, a component of the OpenVMS Debugger which permits a programmer to debug the memory allocation pool.) As for the technique variously known as "fenceposts", the implementation of fenceposts in the allocation headers allows (easier) detection of the typical memory management corruptions -- this also generally requires a core set of memory allocation and deallocation routines be implemented and used. (This common memory management code is a Good Thing, of course.) This "fencepost" technique does "waste" memory when enabled, but any programmer that has ever chased a memory corruption bug will understand why this memory use is valuable. Here is what the layout of the typical memory packet looks like, when fenceposts are implemented. +--------+--------+--------+--------+ | forward pointer (offset or abs) |\ +--------+--------+--------+--------+ > fixed overhead | size of this block in bytes |/ +--------+--------+--------+--------+ | fencepost -- used to detect pool | + corruption due to underwriting + | or overwriting allocated areas. | *========*========*========*========* # userdata (assume 9 bytes) #\ * * \ # # > variable size user data *========*========*========* * / | variable fencepost # #/ + *========* | |> wasted_bytes +--------+--------+--------+--------+ | the fencepost should be filled | + with known value(s) with as few + | zero or space bytes as possible | +--------+--------+--------+--------+ The allocation and deallocation routines fill in the fencepost and read out and compare the values with the expected values, of course. The OpenVMS Wizard tends to use the allocation address and the size of the packet as the values written into the fencepost, and particularly a bit inversion (or an XOR with a known value) of these values -- the bit inversion or the XOR avoids having zero bytes in the fencepost, as zeros are a byte value common found in data overruns and data underruns. Also useful in the common code is the use of the lib$get_vm and lib$free_vm calls -- why? Because zones can be tailored with far more control than with malloc and free, and because this can also provide direct access to the lib$show_vm and lib$stat_vm calls, calls which are helpful when examining and debugging and tuning memory allocation. (Calls to malloc and free are obviously best and most appropriate for code that needs to be portable, of course.) Please see topics (2624), (3115) , (6870) and (1661) for related discussions.
|