Skip to content
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

Various checks on malloc #1781

Merged
merged 16 commits into from
Jun 21, 2016
Merged

Conversation

wiredfool
Copy link
Member

I'd like feedback on this.

i think this is a good approach -- it follows the other changes that we've made to check for overflow on the mallocs. The biggest change is switching to calloc(n,size) instead of malloc(size) in some cases -- calloc does the multiplication, (is supposed to) check for overflow, and zeros the memory. I don't think that the zeroing is ever a bad thing, and there are some reports of the performance of calloc zeroing the memory faster than the memset that we use in a few places.

i will probably rebase/squash this prior to merging.

@coveralls
Copy link

Coverage Status

Coverage decreased (-0.03%) to 76.852% when pulling 0239830 on wiredfool:malloc_check into 34c80ef on python-pillow:master.

@coveralls
Copy link

Coverage Status

Changes Unknown when pulling 0564fb8 on wiredfool:malloc_check into * on python-pillow:master*.

@wiredfool wiredfool modified the milestone: 3.3.0 Apr 1, 2016
int n, i;
int bands;
Py_ssize_t n;
int i, bands;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should i also be Py_ssize_t?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, n is set to constant values well within the range of int in different locations in this function, and i counts up to that. The reason that n is a Py_ssize_t is that it's address is passed as an argument to getlist, which needs it to be a Py_ssize_t to match the result type of the python get sequence length function.

@hugovk
Copy link
Member

hugovk commented Apr 3, 2016

👍 calloc rather than malloc sounds sensible.

@wiredfool wiredfool force-pushed the malloc_check branch 2 times, most recently from 0239830 to 29ab316 Compare April 15, 2016 18:51
@wiredfool
Copy link
Member Author

wiredfool commented Apr 19, 2016

undone -

  • rebase
  • check coverage to make sure we're hitting the checks.

@wiredfool
Copy link
Member Author

@homm Can you take a look at this PR and comment?

@homm
Copy link
Member

homm commented Jun 8, 2016

Ok. I will in couple of days.

@homm
Copy link
Member

homm commented Jun 8, 2016

In general, I'm against replacing malloc with calloc everywhere just for the overflow check. calloc not only does the check, it also guarantees that memory is filled with zeroes, and sometimes this can be slower.

I will take a close look at every case later.

@wiredfool
Copy link
Member Author

My argument for calloc is:

  1. It is safer WRT overflow.
  2. It eliminates an entire class of error with uninitialized memory.
  3. It's not significantly slower, at least tests aren't taking a significantly different time.

I think that the only possible drawback is performance, but from my reading elsewhere calloc is likely faster than malloc + memset, and not significantly slower than malloc due to tricks that can be played with page mapping.

@wiredfool
Copy link
Member Author

Also, when looking at this, I think there's a significant speed gain to be made for larger images by combining Storage.c:ImagingNewBlock and ImagingNewArray so that we're always allocating blocks rather than switching from one block at <16M and line by line for images >16M. For example, in the current layout if you had a 2047x2048 RGBA image, that would be one big allocation of ~16M. a 2049x2048 RGBA would be 2049 allocations of 8K each.

I think it would be relatively transparent to the rest of the code if: char *im->block became char ** im->blocks. We'd add a count of blocks in the struct, and fill the im->image as normal.

@homm
Copy link
Member

homm commented Jun 8, 2016

so that we're always allocating blocks

Thinking about this long time, haven't implemented yet :)

You are right that it should be transparent for most of C code except the code which directly uses ImagingNewBlock (in map.c).

@homm
Copy link
Member

homm commented Jun 8, 2016

I haven't researched, but my thought are optimal block size can be about:

block_size = 2 * 2**20
mem_size = ((block_size + line_size - 1) // line_size) * line_size

@wiredfool
Copy link
Member Author

Looks like the only call to it is through the _imaging.c new_block interface, from ImageTk.py. Which bypasses the threshold check to ensure that they get one big chunk of memory to pass to tk.

So the comment in ImagingNewBlock is wrong, and we should probably be returning an error where we're returning NULL from the overflow check. And ImagingDelete might need to be called on im to prevent a leak.

@@ -376,12 +387,14 @@ getlist(PyObject* arg, int* length, const char* wrong_length, int type)
}

n = PyObject_Length(arg);
if (length && wrong_length && n != *length) {
if (length && wrong_length && n != *length) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inadvertent

@homm
Copy link
Member

homm commented Jun 14, 2016

I've stopped in the middle. It's too large for one pass.

@wiredfool
Copy link
Member Author

No worries.

I've done a quick benchmark on malloc vs calloc, where I opened and loaded a jpeg 100x, one of them was over the block limit and one was under. The difference between this branch (calloc) and master (malloc) is essentially lost in the noise. I'm seeing variances from run to run of ~1% with no clear pattern of one being faster. Calloc seems faster on the larger image, malloc on the smaller, but still pretty well within the noise level.

@wiredfool
Copy link
Member Author

Comments addressed.

@homm
Copy link
Member

homm commented Jun 20, 2016

Ok, I finished with the rest of changes.

@wiredfool wiredfool merged commit bdd0a6a into python-pillow:master Jun 21, 2016
@wiredfool wiredfool deleted the malloc_check branch October 2, 2017 13:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants