discuss-gnustep
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Crash in countByEnumeratingWithState method of GNUstep's implementat


From: John W Kennedy
Subject: Re: Crash in countByEnumeratingWithState method of GNUstep's implementation of NSArray
Date: Wed, 8 Jan 2014 13:56:55 -0500
User-agent: Unison/2.1.10

On 2014-01-08 16:41:36 +0000, Quentin Mathé said:

Le 8 janv. 2014 à 14:02, Richard Frith-Macdonald a écrit :

On 8 Jan 2014, at 12:34, Mathias Bauer <mathias_bauer@gmx.net> wrote:

Am 08.01.14 13:21, schrieb Quentin Mathé:

Le 8 janv. 2014 à 10:45, Mathias Bauer a écrit :

it seems that the implementation of countByEnumeratingWithState in NSArray is broken.

The following code in NSArray.m

{
NSUInteger size = [self count];
NSInteger count;

/* This is cached in the caller at the start and compared at each
* iteration.   If it changes during the iteration then
* objc_enumerationMutation() will be called, throwing an exception.
*/
state->mutationsPtr = (unsigned long *)size;

of course crashes as soon as any fast enumeration is executed for any collection deriving from NSArray. The cast in the last line can't work.

Now I'm wondering how this problem could remain undiscovered or at least unfixed for such a long time. I doubt that everybody who implemented a class that derives from NSArray also re-implemented this method.

I just stumbled on it today while testing some custom NSArray subclass. I think most people don't write NSArray subclass, and GNUstep concrete subclasses are all overriding the fast enumeration method, so the default fast enumeration implementation in NSArray was just never executed.

A simple fix would be to add an iVar that gets the result of [self count] each time this method is called and assigning its address to state->mutationsPtr.

The following should be enough to fix it: state->mutationsPtr = (unsigned long *)&size;

I don't use fast enumeration, so I'm unfamiliar with this, but on the basis of looking at this code and reading a little it also seems strange to me to use a stack variable, unless you can somehow guarantee the stack will be left intact while the fast enumeration takes place?

Yes, using a stack variable doesn't make sense. state->mutationsPtr would probably point to some random content in memory once -countWithEnumeratingWithState: returns.

I wonder if the pointer should simply be set to self? That would presumably point to something guaranteed to exist for the duration fo the enumeration and presumably to remain unchanged. This ought to be fine for NSArray which is never mutated.

That sounds reasonable.

I don't understand the point of using an ivar though. Leaving aside the fact that NSArray is an abstract class and is not supposed/allowed to have ivars, what would be the point? As I understand it the pointer is supposed to be set to point to some value which will change if the collection is mutated ... that should never happen for NSArray and if someone implements an NSMutableArray subclass they would presumably want to implement their own storage and mechanism to indicate when their contents had changed. So it looks to me like subclasses of NSMutableArray ought to be implementing their own version of this method.

Of course, you may know better ... have Apple added an API to NSMutableArray which allows a subclass to mark instances of itsself as mutated?

It seems there is no such API.

I was suggesting in my previous reply to move the _version ivar from GSMutableArray to NSMutableArray, but as you pointed it out, this won't help since overriden primitive methods (e.g. -addObject:) need to increment this variable. And Apple doesn't declare any similar protected ivar or special method for this purpose.

I tested writing a custom NSMutableArray subclass and mutating it during fast enumeration on Mac OS X, and I get no mutation-related exceptions, so I guess NSMutableArray inherit -countByEnumeratingWithState: from NSArray in Cocoa (if you don't override it).

In Apple, the key field seems to be field unsigned long *mutationsPtr in NSFastEnumerationState, documented only as "Arbitrary state information used to detect whether the collection has been mutated."

--
John W Kennedy
"But now is a new thing which is very old--
that the rich make themselves richer and not poorer,
which is the true Gospel, for the poor's sake."
 -- Charles Williams.  "Judgement at Chelmsford"



reply via email to

[Prev in Thread] Current Thread [Next in Thread]