2

This code mimics some image processing with malloced memory, it's a distilled example of a problem. It runs fine if optimized at other levels including "Fastest Smallest", but fails on GCC_OPTIMIZATION_LEVEL = 3 AKA Fastest [-03] and Fastest Aggressive. It crashes only on the device, seen on a 6,5s,5 and various IOS 9.3, 8.4.

There's something about the allocation sizes that aggravates the issue. There are some notes in the code about what helps make it fail.

Reproduce by creating an single view app project, set the optimization level to "Fastest" and paste this code into main and call it from inside the autorelease pool, or paste it in the view controller and call it from viewDidLoad or anywhere you like.

The debugger isn't very useful with optimizations turned on, but the crash comes in the while loop at "*writeIter = readIter->d;" a EXC_BAD_ACCESS code=1

So that tells me it's reading and the address that triggers the EXC_BAD_ACCESS is the same as readEnd. That should never happen as that's the condition the while is supposed to prevent... optimizer bug or stupid mistake?

#import <stdlib.h>
#import <stdio.h>

/**
        Requires this to fail   -> GCC_OPTIMIZATION_LEVEL = 3
        This won't do it        -> GCC_OPTIMIZATION_LEVEL = s
 */

typedef struct  {
    unsigned char a, b, c, d;
} foo;

void boom()
{
    char* memory[1000];
    // these sizes are important to reproducing this issue, changing them by +-1 will make it go away
    int height = 960;   //480,960,1920
    int width = 1280;   //640,1280,2560
    int depth = sizeof(foo);

    printf("height = %d, width = %d, total = %d\n\n", height, width, height*width*depth);
    for (int i = 0; i < 1000; ++i)
    {
        memory[i] = malloc(20000);  // allocate memory to force the allocations of readBuf and writeBuf to move, numbers
                                    // less than 15k don't effect the alloced addresses of the bufs, so we keep getting
                                    // the same ones and no boom.
        foo*    readBuf = malloc(height*width*depth);
        unsigned char*  writeBuf = malloc(height*width);    // smaller than read

        foo     *readIter = readBuf;
        foo     *readEnd = readBuf + height*width;          // only read size of smaller

        unsigned char* writeIter = writeBuf;

        printf("test: i = %d, readIter = %p, readEnd = %p, writeIter = %p\n", i, readIter, readEnd, writeIter);

        while (readIter < readEnd)
        {
            *writeIter = readIter->d;   // you died here during a read, and readIter == readEnd, look at the EXC_BAD_ACCESS address
                                        // (printfed) it's readEnd, and that isn't supposed to happen with the conditional.
            ++writeIter;
            ++readIter;
        }

        free(readBuf);
        free(writeBuf);
    }

    for (int i = 0; i < 1000; ++i)
    {
        free(memory[i]);
    }
}
Cœur
  • 37,241
  • 25
  • 195
  • 267
troppoli
  • 558
  • 6
  • 13
  • hummm I've only tried it on the 6,5s and 5. Try bumping up the memory size from 20000. The goal being to prevent the same address being returned for readbuf from malloc on each loop iteration. I'm specifically on 7.3.1. You can also use the larger pair of sizes 1920 and 2560 – troppoli Jun 10 '16 at 19:41
  • You'll know that the address is changing by looking at the console, and the address of readIter is changing. Don't forget the GCC_OPTIMIZATION_LEVEL = 3 AKA Fastest [-03] – troppoli Jun 10 '16 at 19:57
  • It repros, but I have no idea what is wrong. I don't trust anything I see in the debugger. I looked at the bad-access address in the memory view and it looked fine. – Lou Franco Jun 10 '16 at 20:37
  • The printfs do tell some of the story, and you'll see that readIter == readEnd when it dies, but at no other time. So it's reading off the end and avoiding the conditional. – troppoli Jun 10 '16 at 20:49
  • @LouFranco for reference, what tweaks did you make for the 6s? Oh and can you delete the first comment, it might discourage others from looking. Thanks! – troppoli Jun 10 '16 at 21:26
  • `char* memory[10000];` `memory[i] = malloc(80000);` -- didn't try to minimize -- just kept increasing by big jumps until it crashed in the same place. – Lou Franco Jun 13 '16 at 13:31
  • I do not see definitely that `readIter == readEnd` at death. I added an if to check for that and it never goes in. I believe it's a bug in optimizer - you need to read the assembly to be sure, but worth opening a bug, I think. – Lou Franco Jun 13 '16 at 13:34
  • I added the same 'if', and think that adding it defeats the optimizer! I'm not a modern day assembly reader, so I'm out of luck on determining what's going on by looking at it. I'll open a DTS request... – troppoli Jun 13 '16 at 13:40
  • Me either -- good luck. The thing that I don't understand is that if I look at the memory location where the BAD ACCESS happens, it looks mapped. I believe the BAD_ACCESS is real -- usually you can see that in the memory view – Lou Franco Jun 13 '16 at 13:47
  • Possible duplicate of [Crash with alloca function but ok with malloc](http://stackoverflow.com/questions/22693328/crash-with-alloca-function-but-ok-with-malloc) – Paul Sweatte Feb 08 '17 at 16:59

0 Answers0