View Issue Details

IDProjectCategoryView StatusLast Update
0002897FSSCPpublic2013-07-07 22:46
ReporterEchelon9 Assigned ToEchelon9  
PrioritynormalSeverityminorReproducibilityhave not tried
Status resolvedResolutionfixed 
Product Version3.7.0 RC2 
Target Version3.7.0 
Summary0002897: Heap corruption in dock_evaluate_all_docked_objects() objectdock.cpp
DescriptionAs reported by Droid803 on HLP forums here : http://www.hard-light.net/forums/index.php?topic=84900.0

Appears to be caused by the following vm_free in dock_evaluate_all_docked_objects() per callstack

======================================

// evaluate a certain function for all docked objects
void dock_evaluate_all_docked_objects(object *objp, dock_function_info *infop, void (*function)(object *, dock_function_info *))
{
    Assert((objp != NULL) && (infop != NULL) && (function != NULL));

    // not docked?
    if (!object_is_docked(objp))
    {
        // call the function for just the one object
        function(objp, infop);
        return;
    }
    ...
    // we have multiple objects docked and we must treat them as a tree
    else
    {
        // create a bit array to mark the objects we check
        ubyte *visited_bitstring = (ubyte *) vm_malloc(calculate_num_bytes(Num_objects));

        // clear it
        memset(visited_bitstring, 0, calculate_num_bytes(Num_objects));

        // start evaluating the tree
        dock_evaluate_tree(objp, infop, function, visited_bitstring);

        // destroy the bit array
        vm_free(visited_bitstring);
        visited_bitstring = NULL;
    }
}
Steps To ReproduceUnsure, may have something to do with warping in objects that are docked together.
Additional InformationERROR: AddressSanitizer: heap-buffer-overflow on address 0x6020000e711f at pc 0x101a70814 bp 0x7fff5fbfbfb0 sp 0x7fff5fbfbfa8
READ of size 1 at 0x6020000e711f thread T0
    #0 0x101a70813 in dock_evaluate_tree, unsigned char*) objectdock.cpp:350
    0000001 0x101a68129 in dock_evaluate_all_docked_objects) objectdock.cpp:339
    0000002 0x101a714cf in dock_move_docked_objects objectdock.cpp:397
    0000003 0x101a90703 in obj_move_all object.cpp:1467
    0000004 0x1002f06bc in game_simulation_frame freespace.cpp:3985
    0000005 0x1002f53c0 in game_frame freespace.cpp:4378
    0000006 0x1002fabdb in game_do_frame freespace.cpp:4789
    0000007 0x100306736 in game_do_state freespace.cpp:6465
    0000008 0x100901f28 in gameseq_process_events gamesequence.cpp:405
    0000009 0x10030cfd6 in game_main freespace.cpp:7032
    0000010 0x10030e674 in SDL_main freespace.cpp:7166
    #11 0x100002c33 in -[SDLMain applicationDidFinishLaunching:] SDLMain.m:300
    0000012 0x7fff95444ed9 in _CFXNotificationPost (in CoreFoundation) + 2553
    0000013 0x7fff8f5c67b5 in -[NSNotificationCenter postNotificationName:object:userInfo:] (in Foundation) + 63
    0000014 0x7fff98e5c52c in -[NSApplication _postDidFinishNotification] (in AppKit) + 291
    0000015 0x7fff98e5c265 in -[NSApplication _sendFinishLaunchingNotification] (in AppKit) + 215
    0000016 0x7fff98e59451 in -[NSApplication(NSAppleEventHandling) _handleAEOpenEvent:] (in AppKit) + 565
    0000017 0x7fff98e5904b in -[NSApplication(NSAppleEventHandling) _handleCoreEvent:withReplyEvent:] (in AppKit) + 350
    0000018 0x7fff8f5e007a in -[NSAppleEventManager dispatchRawAppleEvent:withRawReply:handlerRefCon:] (in Foundation) + 307
    0000019 0x7fff8f5dfedc in _NSAppleEventManagerGenericHandler (in Foundation) + 105
    0000020 0x7fff94e91077 in aeDispatchAppleEvent(AEDesc const*, AEDesc*, unsigned int, unsigned char*) (in AE) + 306
    0000021 0x7fff94e90ed8 in dispatchEventAndSendReply(AEDesc const*, AEDesc*) (in AE) + 36
    0000022 0x7fff94e90d98 in aeProcessAppleEvent (in AE) + 317
    0000023 0x7fff93c24708 in AEProcessAppleEvent (in HIToolbox) + 99
    0000024 0x7fff98e55835 in _DPSNextEvent (in AppKit) + 1455
    0000025 0x7fff98e54df1 in -[NSApplication nextEventMatchingMask:untilDate:inMode:dequeue:] (in AppKit) + 127
    0000026 0x7fff98e4c1a2 in -[NSApplication run] (in AppKit) + 516
    0000027 0x100004da2 in CustomApplicationMain SDLMain.m:227
    0000028 0x10000480f in main SDLMain.m:377
    0000029 0x100001953 in start (in FS2_Open (debug)) + 51
    0000030 0x0 in 0x0
0x6020000e711f is located 0 bytes to the right of 15-byte region [0x6020000e7110,0x6020000e711f)
allocated by thread T0 here:
    #0 0x1053d88f1 in wrap_malloc _asan_rtl_
    0000001 0x1025f4828 in _vm_malloc stubs.cpp:571
    0000002 0x101a67ec2 in dock_evaluate_all_docked_objects) objectdock.cpp:333
    0000003 0x101a714cf in dock_move_docked_objects objectdock.cpp:397
    0000004 0x101a90703 in obj_move_all object.cpp:1467
    0000005 0x1002f06bc in game_simulation_frame freespace.cpp:3985
    0000006 0x1002f53c0 in game_frame freespace.cpp:4378
    0000007 0x1002fabdb in game_do_frame freespace.cpp:4789
    0000008 0x100306736 in game_do_state freespace.cpp:6465
    0000009 0x100901f28 in gameseq_process_events gamesequence.cpp:405
    0000010 0x10030cfd6 in game_main freespace.cpp:7032
    #11 0x10030e674 in SDL_main freespace.cpp:7166
    0000012 0x100002c33 in -[SDLMain applicationDidFinishLaunching:] SDLMain.m:300
    0000013 0x7fff95444ed9 in _CFXNotificationPost (in CoreFoundation) + 2553
    0000014 0x7fff8f5c67b5 in -[NSNotificationCenter postNotificationName:object:userInfo:] (in Foundation) + 63
    0000015 0x7fff98e5c52c in -[NSApplication _postDidFinishNotification] (in AppKit) + 291
    0000016 0x7fff98e5c265 in -[NSApplication _sendFinishLaunchingNotification] (in AppKit) + 215
    0000017 0x7fff98e59451 in -[NSApplication(NSAppleEventHandling) _handleAEOpenEvent:] (in AppKit) + 565
    0000018 0x7fff98e5904b in -[NSApplication(NSAppleEventHandling) _handleCoreEvent:withReplyEvent:] (in AppKit) + 350
    0000019 0x7fff8f5e007a in -[NSAppleEventManager dispatchRawAppleEvent:withRawReply:handlerRefCon:] (in Foundation) + 307
    0000020 0x7fff8f5dfedc in _NSAppleEventManagerGenericHandler (in Foundation) + 105
    0000021 0x7fff94e91077 in aeDispatchAppleEvent(AEDesc const*, AEDesc*, unsigned int, unsigned char*) (in AE) + 306
    0000022 0x7fff94e90ed8 in dispatchEventAndSendReply(AEDesc const*, AEDesc*) (in AE) + 36
    0000023 0x7fff94e90d98 in aeProcessAppleEvent (in AE) + 317
    0000024 0x7fff93c24708 in AEProcessAppleEvent (in HIToolbox) + 99
    0000025 0x7fff98e55835 in _DPSNextEvent (in AppKit) + 1455
    0000026 0x7fff98e54df1 in -[NSApplication nextEventMatchingMask:untilDate:inMode:dequeue:] (in AppKit) + 127
    0000027 0x7fff98e4c1a2 in -[NSApplication run] (in AppKit) + 516
    0000028 0x100004da2 in CustomApplicationMain SDLMain.m:227
    0000029 0x10000480f in main SDLMain.m:377
Shadow bytes around the buggy address:
  0x1c040001cdd0: fa fa fd fa fa fa fd fa fa fa fd fd fa fa 00 04
  0x1c040001cde0: fa fa 00 00 fa fa fa fa fa fa 00 00 fa fa fa fa
  0x1c040001cdf0: fa fa fa fa fa fa 00 fa fa fa 00 00 fa fa fa fa
  0x1c040001ce00: fa fa fd fd fa fa fa fa fa fa fa fa fa fa fa fa
  0x1c040001ce10: fa fa fd fd fa fa fa fa fa fa fa fa fa fa fd fd
=>0x1c040001ce20: fa fa 00[07]fa fa fd fd fa fa fa fa fa fa fa fa
  0x1c040001ce30: fa fa fa fa fa fa 04 fa fa fa 00 fa fa fa 00 00
  0x1c040001ce40: fa fa fd fa fa fa fa fa fa fa 00 00 fa fa fd fd
  0x1c040001ce50: fa fa fd fd fa fa fd fd fa fa 00 00 fa fa 00 00
  0x1c040001ce60: fa fa fd fd fa fa 00 00 fa fa 00 fa fa fa fd fd
  0x1c040001ce70: fa fa 00 fa fa fa fd fa fa fa fd fa fa fa fd fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable: 00
  Partially addressable: 01 02 03 04 05 06 07
  Heap left redzone: fa
  Heap right redzone: fb
  Freed heap region: fd
  Stack left redzone: f1
  Stack mid redzone: f2
  Stack right redzone: f3
  Stack partial redzone: f4
  Stack after return: f5
  Stack use after scope: f8
  Global redzone: f9
  Global init order: f6
  Poisoned by user: f7
  ASan internal: fe
==15124==ABORTING
TagsNo tags attached.

Relationships

related to 0002900 resolvedEchelon9 Potential uninitialised AI code structures -- dangerous use of Num_objects 

Activities

Echelon9

2013-06-30 05:41

developer  

Droid803_fs2_open.log (69,846 bytes)

Goober5000

2013-07-01 01:58

administrator   ~0015152

I would want to see the mission: specifically when the crash occurs and what the value of Num_objects is at the time of the crash. It occurred to me that if this function were evaluated when Num_objects was still -1 (which would supposedly be at the beginning of the mission before any objects were created), the size of the bitstring would be 0, and therefore any writes would be off the end of the array.

Goober5000

2013-07-01 02:00

administrator   ~0015153

The core problem would have to be that there were more objects in the dock tree than Num_objects. That would cause the bitstring to be too short for navigating the tree.

The question, of course, is how that could possibly occur.

Echelon9

2013-07-01 14:37

developer   ~0015154

Either AddressSanitizer or the LLVM sibling IOC (integer overflow checker) should pick this up if we can repro it.

Echelon9

2013-07-07 07:27

developer   ~0015163

Have been able to reproduce this in AddressSanitizer using private data shared by Droid. Additional Information has been updated.

Currently working through the memory steps leading up to the crash.

Echelon9

2013-07-07 08:09

developer   ~0015164

Last edited: 2013-07-07 08:22

Somehow objects are getting added to the Objects[] array without increasing Num_objects to track the same.

At the time of vm_malloc()'ing visited_bitstring in dock_evaluate_all_docked_objects()
===========================================
calculate_num_bytes(Num_objects) = 15, and
Num_objects = 113

At the time of calling get_bit() in dock_evaluate_tree() <- the crash
===========================================
trying to access visited_bitstring[15],
OBJ_INDEX(objp) = 121, and
Num_objects = 113

i.e. as Goober predicted, that OBJ_INDEX(objp) > Num_objects is the issue

Echelon9

2013-07-07 08:30

developer  

mantis-2897-temp-fix.patch (712 bytes)   
Index: code/object/objectdock.cpp
===================================================================
--- code/object/objectdock.cpp	(revision 9699)
+++ code/object/objectdock.cpp	(working copy)
@@ -330,10 +330,10 @@
 	else
 	{
 		// create a bit array to mark the objects we check
-		ubyte *visited_bitstring = (ubyte *) vm_malloc(calculate_num_bytes(Num_objects));
+		ubyte *visited_bitstring = (ubyte *) vm_malloc(calculate_num_bytes(Num_objects + 20));
 
 		// clear it
-		memset(visited_bitstring, 0, calculate_num_bytes(Num_objects));
+		memset(visited_bitstring, 0, calculate_num_bytes(Num_objects + 20));
 
 		// start evaluating the tree
 		dock_evaluate_tree(objp, infop, function, visited_bitstring);
mantis-2897-temp-fix.patch (712 bytes)   

Echelon9

2013-07-07 08:31

developer   ~0015165

Last edited: 2013-07-07 08:32

Attaching a temporary fix patch -- this ensures the visited_bitstring bit array is setup with a slightly larger size than initially expected.

If we want to get a temporary fix into 3.7.0 Final, this may be appropriate.

Continuing to get to the bottom of this.

Echelon9

2013-07-07 11:50

developer  

mantis-2897-resolution.patch (720 bytes)   
Index: code/object/objectdock.cpp
===================================================================
--- code/object/objectdock.cpp	(revision 9706)
+++ code/object/objectdock.cpp	(working copy)
@@ -330,10 +330,10 @@
 	else
 	{
 		// create a bit array to mark the objects we check
-		ubyte *visited_bitstring = (ubyte *) vm_malloc(calculate_num_bytes(Num_objects));
+		ubyte *visited_bitstring = (ubyte *) vm_malloc(calculate_num_bytes(Highest_object_index));
 
 		// clear it
-		memset(visited_bitstring, 0, calculate_num_bytes(Num_objects));
+		memset(visited_bitstring, 0, calculate_num_bytes(Highest_object_index));
 
 		// start evaluating the tree
 		dock_evaluate_tree(objp, infop, function, visited_bitstring);

Echelon9

2013-07-07 11:55

developer   ~0015166

Patch attached for code review that undertakes the proper resolution. This is ready for 3.7.0 subject to review comments.

========================

The underlying issue is that Num_objects is an inappropriate measure to use, when we have a non-compacting Objects garbage collection algorithm.

Num_objects does correctly track the in use items in the Objects[] array. However, the index of the highest in use item in the Objects[] array can be well above the value set at Num_objects, particularly where there has been many objects initialised and then deleted (as was the case in Droid's mission).

A value that works for any algorithm that later uses OBJ_INDEX() to walk the Objects[] array is a measure of high water mark item usage in Objects.

Such a value is stored in Highest_object_index.

Until such time as we implement a compacting garbage collection memory system, care should be used using Num_objects outside of the object allocation and free'ing functions.

========================

To note there is one other possible dangerous use of Num_objects in the code base within init_ai_objects(). I will open a second Mantis report for that issue.

Goober5000

2013-07-07 22:44

administrator   ~0015169

Great catch. :yes: I probably designed the code with the assumption that Num_objects represented the upper bound of an array that was always compacted. But you're right, that's an incorrect assumption.

I think, however, that I'll change the calculation to use MAX_OBJECTS. That will put it in sync with parsedockedobject.cpp which uses Parse_objects.size(). It will also move some of the run-time calculation into compile-time calculation (which will save an insignificant amount of time, but still).

The other thing to keep in mind is that when you have a 0-based array, the highest index is always one less than the length. So if we had used Highest_object_index, we would have had to add a +1.

I'll commit this and assign credit to you for doing the footwork.

Goober5000

2013-07-07 22:46

administrator   ~0015170

Fix committed to trunk@9708.

Related Changesets

fs2open: trunk r9708

2013-07-07 19:50

Goober5000


Ported: N/A

Details Diff
a modified version of Echelon9's fix for Mantis 0002897 (heap corruption due to object indexes being too large for the object dock bitstring) Affected Issues
0002897
mod - /trunk/fs2_open/code/object/objectdock.cpp Diff File

Issue History

Date Modified Username Field Change
2013-06-30 05:41 Echelon9 New Issue
2013-06-30 05:41 Echelon9 File Added: Droid803_fs2_open.log
2013-06-30 12:45 Echelon9 Assigned To => Echelon9
2013-06-30 12:45 Echelon9 Status new => assigned
2013-07-01 01:58 Goober5000 Note Added: 0015152
2013-07-01 02:00 Goober5000 Note Added: 0015153
2013-07-01 14:37 Echelon9 Note Added: 0015154
2013-07-07 07:26 Echelon9 Additional Information Updated
2013-07-07 07:27 Echelon9 Note Added: 0015163
2013-07-07 07:27 Echelon9 Status assigned => acknowledged
2013-07-07 08:09 Echelon9 Note Added: 0015164
2013-07-07 08:21 Echelon9 Note Edited: 0015164
2013-07-07 08:22 Echelon9 Note Edited: 0015164
2013-07-07 08:30 Echelon9 File Added: mantis-2897-temp-fix.patch
2013-07-07 08:31 Echelon9 Note Added: 0015165
2013-07-07 08:31 Echelon9 Assigned To Echelon9 => Goober5000
2013-07-07 08:31 Echelon9 Status acknowledged => code review
2013-07-07 08:32 Echelon9 Note Edited: 0015165
2013-07-07 11:50 Echelon9 File Added: mantis-2897-resolution.patch
2013-07-07 11:55 Echelon9 Note Added: 0015166
2013-07-07 12:06 Echelon9 Relationship added related to 0002900
2013-07-07 22:44 Goober5000 Note Added: 0015169
2013-07-07 22:44 Goober5000 Assigned To Goober5000 => Echelon9
2013-07-07 22:46 Goober5000 Changeset attached => fs2open trunk r9708
2013-07-07 22:46 Goober5000 Note Added: 0015170
2013-07-07 22:46 Goober5000 Status code review => resolved
2013-07-07 22:46 Goober5000 Resolution open => fixed