Closed Bug 431475 Opened 16 years ago Closed 16 years ago

KeyEquivDBItem shouldn't use NSMutableIndexSet

Categories

(Core :: Widget: Cocoa, defect)

x86
macOS
defect
Not set
normal

Tracking

()

RESOLVED FIXED

People

(Reporter: bent.mozilla, Assigned: bent.mozilla)

Details

Attachments

(2 files, 4 obsolete files)

Attached file Stack trace
It appears that the value of an index in NSMutableIndexSet is limited to NSNotFound - 1 (where NSNotFound is 0x7fffffff according to http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Miscellaneous/Foundation_Constants/Reference/reference.html).

This means that pointers like mine (0xf92e0fd0) will throw an exception and kill the app. See the attached stack trace.
Flags: blocking1.9+
Attached patch Patch, v1 (obsolete) — Splinter Review
Obj-C really isn't my strongest language, but I think this might work.
Assignee: joshmoz → bent.mozilla
Status: NEW → ASSIGNED
Attachment #318549 - Flags: review?(joshmoz)
Comment on attachment 318549 [details] [diff] [review]
Patch, v1

+  return [mTables containsObject:[NSValue valueWithPointer:aTable]];

Are you sure this operation uses an equality test instead of the specific pointer you pass in? If not, you're always going to get NO here. You might want to be using the "member:" method of NSSet, which explicitly uses an equality test.

+    [mTables removeObject:[NSValue valueWithPointer:aTable]];

Are you sure this operation uses an equality test instead of the specific pointer you pass in? If not, you're never going to remove objects.

+    ItemValue *itemValue = (ItemValue *)[NSValue valueWithPointer:aItem];

Will this cast work correctly when the object is not actually of type ItemValue? If you called "isKindOfClass:[ItemValue class]" on the object you got back it would be false.

Since you're really using the isEqual method of ItemValue like a utility function, maybe you should just make it a utility function and use that when you need that type of comparison.
> It appears that the value of an index in NSMutableIndexSet is
> limited to NSNotFound - 1

This is appalling!  And Apple doesn't own up to it anywhere (as far as
I can tell).

But you can argue that Apple's docs imply it, and it does appear to be
true (from the evidence of your crash log).

Good catch!
> It appears that the value of an index in NSMutableIndexSet is
> limited to NSNotFound - 1

On Leopard NSNotFound is defined as NSIntegerMax, which in turn is defined as LONG_MAX.  I did all my compiling and tests on Leopard, which is probably why I didn't see this problem myself.
> On Leopard NSNotFound is defined as NSIntegerMax, which in turn is
> defined as LONG_MAX.  I did all my compiling and tests on Leopard,
> which is probably why I didn't see this problem myself.

And I was compiling on a 64-bit system ... though I wasn't compiling
64-bit binaries.  (On 32-bit systems LONG_MAX _is_ 0x7fffffff.)
>> It appears that the value of an index in NSMutableIndexSet is
>> limited to NSNotFound - 1
>
> This is appalling!  And Apple doesn't own up to it anywhere (as far
> as I can tell).

What's really appalling is that everywhere in Apple's docs on
NSIndexSet and NSMutableIndexSet, the indexes are claimed to be
unsigned integers (NSUInteger).  But this (apparently) isn't true --
they're really signed integers (NSInteger).
Attached patch Patch, v2 (obsolete) — Splinter Review
The docs don't say anything about how it determines equality so I assume that it just uses pointer comparisons :(

This patch does everything manually.
Attachment #318549 - Attachment is obsolete: true
Attachment #318660 - Flags: review?(joshmoz)
Attachment #318549 - Flags: review?(joshmoz)
Attached patch Patch, v3 (obsolete) — Splinter Review
Tweaked the way KeyEquivDBItem::removeTable method worked and fixed a style nit I knew you were going to comment on :)
Attachment #318660 - Attachment is obsolete: true
Attachment #318661 - Flags: review?(joshmoz)
Attachment #318660 - Flags: review?(joshmoz)
Comment on attachment 318661 [details] [diff] [review]
Patch, v3

We don't want the exception handling macros there because that code runs inserted into Apple's framework code and we don't want to interfere with their own exceptions.

Maybe we could leave the exception handling there if it didn't wrap the swizzled methods, just what we're doing ourselves.
By the way, I'm still not confident the membership comparisons will be done correctly.  As far as I know, the only way to be sure is to override the isEqual and (probably also) the hash methods of the object to be stored.  (I forgot about overriding the hash method in my original code.)
"member:" uses "isEqual:", which is not the same as "isEqualToValue:". We might not be able to do that unless we override "isEqual:" to be the same as "isEqualToValue:".
I think we should do something like the following:

@interface ItemValue : NSValue
- (NSUInteger)hash;
- (BOOL)isEqual:(id)anObject;
@end
 
@implementation ItemValue

- (NSUInteger)hash
{
  return (NSUInteger) [self pointerValue];
}
 
// Ensure that hastable comparisons use an NSValue object's value -- not its
// address in memory.
- (BOOL)isEqual:(id)anObject
{
  if ([anObject isKindOfClass:[NSValue class]])
    return ([self pointerValue] == [(NSValue*)anObject pointerValue]);
  return [super isEqual:anObject];
}

@end
Or actually this'd be better:

// Ensure that hastable comparisons use an NSValue object's value -- not its
// address in memory.
- (BOOL)isEqual:(id)anObject
{
  if ([anObject isKindOfClass:[ItemValue class]])
    return ([self pointerValue] == [(ItemValue*)anObject pointerValue]);
  return [super isEqual:anObject];
}
Summarizing from irc: NSValue, like most Foundation objects, has isEqual: behavior that does what you would hope rather than straight address comparison, so there's no need for the override.
Attached patch Patch, v4 (obsolete) — Splinter Review
Ok, I verified that smorgan is correct, NSValue's isEqual does the right thing. This fixes the exception guards to ignore the swizzled methods.
Attachment #318661 - Attachment is obsolete: true
Attachment #318746 - Flags: review?(joshmoz)
Attachment #318661 - Flags: review?(joshmoz)
Comment on attachment 318746 [details] [diff] [review]
Patch, v4

+
+  NS_OBJC_END_TRY_ABORT_BLOCK;
+

Extra newline here at the end of the method, plz get rid of it on checkin.
Attachment #318746 - Flags: superreview?(smichaud)
Attachment #318746 - Flags: review?(joshmoz)
Attachment #318746 - Flags: review+
Attachment #318746 - Flags: superreview?(smichaud) → review?(smichaud)
More quibbles:

If smorgan is right about NSValue's isEqual and hash methods always
"doing the right thing" (and he seems to be), you don't really need
the GetShadowDBKeyForPointer() method.  Instead you can just call

  NSValue *key = [NSValue valueWithPointer:aItem];

This should save a few CPU cycles.

I notice that the Objective-C exception macros are still there, even
though this code is quite isolated from "our" code (it never calls
"our" code, and is never called by it (at least directly)).  With them
present you risk getting crashes on non-fatal errors.

Also, I've had bad luck putting these macros anywhere but at the very
beginning and the very end of a method.  In fact I once resolved a
crash by moving one of these macros to the top of a method (from a
position one line below the top).  Yes, this is pretty vague ... but I
thought I should mention it.
I don't want to be doing anything unexpected in methods we swizzle from the OS, including throwing exceptions. There shouldn't be anything wrong with using those macros in places other than the top and bottom of methods.
So ... are you going to get rid of GetShadowDBKeyForPointer()?

I don't think that's essential, but it could be a factor in performance.

I'm fine with the Objective-C exception macros, by the way.
Attached patch Patch, v5Splinter Review
Attachment #318746 - Attachment is obsolete: true
Attachment #318902 - Flags: review?
Attachment #318746 - Flags: review?(smichaud)
Attachment #318902 - Flags: review? → review?(smichaud)
Comment on attachment 318902 [details] [diff] [review]
Patch, v5

Looks fine to me.
Attachment #318902 - Flags: review?(smichaud) → review+
Attachment #318902 - Flags: superreview?(vladimir)
Attachment #318902 - Flags: superreview?(vladimir) → superreview+
Attachment #318902 - Flags: approval1.9?
Comment on attachment 318902 [details] [diff] [review]
Patch, v5

a=mconnor on behalf of 1.9 drivers
Attachment #318902 - Flags: approval1.9? → approval1.9+
Keywords: checkin-needed
I'll let josh or bent land this since it's their patch.
Keywords: checkin-needed
Yeah, I'll get this in as soon as the tree is green. Josh, thanks for grabbing this yesterday, I was swamped.
Just to be safe, I've tested the current patch (attachment 318902 [details] [diff] [review])
against the two STRs from bug 422827 (in bug 422827 comment #8 and bug
422827 comment #29).  Neither caused a crash.
Fixed!
Status: ASSIGNED → RESOLVED
Closed: 16 years ago
Resolution: --- → FIXED
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: