Skip to content

Fix memory corruption with NSMutableData(length:) #383

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 8, 2016

Conversation

mbvreddy
Copy link
Contributor

NSMutableData(length:) allocates a memory of 'length' bytes and initialises NSMutableData with the same malloced bytes as buffer. And, capacity is 16 (default). And, any further appends to NSMutableData actually happens outside malloced region corrupting the memory next to it.

Fix is to avoid reusing malloced region as buffer. Instead, create new buffer with capacity and copy contents of malloced region into the buffer and free malloced region after copy.

@parkera
Copy link
Contributor

parkera commented May 28, 2016

I think the problem goes deeper than this. NSMutableData should create a CFMutableData and defer its operations to that. The init method we're calling here should always copy the data into the new mutable buffer.

@mbvreddy
Copy link
Contributor Author

@parkera NSMutableData.init() calls boils down to _CFDataInit() which creates CFMutableData already. This PR corrects init method, NSMutableData(length:) to copy the contents into the new CFMutableData buffer.

@parkera
Copy link
Contributor

parkera commented May 31, 2016

So we call malloc and get an uninitialized region, then copy it into another buffer, then free it?

@mbvreddy
Copy link
Contributor Author

mbvreddy commented Jun 1, 2016

@parkera Yes. That is exactly what happens after this fix.

@parkera
Copy link
Contributor

parkera commented Jun 15, 2016

Why copy an uninitialized malloc buffer? It seems like an unnecessary performance cost.

@mbvreddy
Copy link
Contributor Author

@parkera
We have only 2 option, either create buffer in Foundation and ask CoreFoundation to reuse the same buffer or let CoreFoundation create buffer for us and copy the initial content. Logic of initial buffer capacity and the amount it needs to grow further is defined in CoreFoundation.

If we have to avoid copy of malloc'ed data, we need to create buffer at Foundation layer and reuse the same. But, we may not know the capacity of the buffer and we just need to create a buffer of capacity 'length' which will be expanded on first append.

So, its a trade-off between copying malloc'ed content or allow it to grow at first append call.

@phausler
Copy link
Contributor

phausler commented Jul 7, 2016

I wonder if it might be better to just call the plain init method and set the length that way we are less wasteful?

@mbvreddy
Copy link
Contributor Author

mbvreddy commented Jul 8, 2016

@phausler that sounds good and neat. init() would initialize buffer with default capacity (say 16) and length as 0.

CFDataSetLength() would check if capacity is < newlength. If yes, and buffer is growable, it expands it.

let me build it.

@phausler
Copy link
Contributor

phausler commented Jul 8, 2016

Fyi this is the abstract objc version:

- (id)initWithLength:(NSUInteger)length {
    self = [self initWithCapacity:length];
    [self setLength:length];
    return self;
}

@mbvreddy mbvreddy force-pushed the issue160_corruption branch from fc0c0de to 2ff9184 Compare July 8, 2016 17:21
@mbvreddy mbvreddy force-pushed the issue160_corruption branch from 2ff9184 to fef4d19 Compare July 8, 2016 17:48
@mbvreddy
Copy link
Contributor Author

mbvreddy commented Jul 8, 2016

@phausler modified fix and contributed simple testcase as well.

@phausler phausler merged commit 11df1ec into swiftlang:master Jul 8, 2016
@parkera
Copy link
Contributor

parkera commented Jul 8, 2016

Thanks!

kateinoigakukun pushed a commit to kateinoigakukun/swift-corelibs-foundation that referenced this pull request Oct 11, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants