#4948 closed (fixed)
Saving FileField files is not thread-safe
Reported by: | anonymous | Owned by: | Robert Coup |
---|---|---|---|
Component: | Core (Other) | Version: | dev |
Severity: | Keywords: | fs-rf-docs | |
Cc: | Triage Stage: | Accepted | |
Has patch: | no | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | yes |
Easy pickings: | no | UI/UX: | no |
Description
Picking the filename in django.db.modes.Model._save_FIELD_file is not thread-safe. There is an obvious racing condition if one thread will have found a non-existing filename and then a second thread will search for a non-existing name based on the same filename before the first thread starts to write the file.
Because this function is probably most often called from the admin UI and the newforms frameworks doesn't support file upload yet, it isn't critical. Still, this kind of problems can cause difficult to debug problems.
Attachments (5)
Change History (21)
comment:1 by , 17 years ago
Triage Stage: | Unreviewed → Accepted |
---|
comment:2 by , 17 years ago
Owner: | changed from | to
---|---|
Status: | new → assigned |
by , 17 years ago
Attachment: | safe_filename_gen__modelsonly.1.diff added |
---|
comment:3 by , 17 years ago
Malcolm pointed out this needs confirmation on Windows wrt. O_CREAT & O_EXCL
comment:4 by , 17 years ago
Patch needs improvement: | set |
---|
Tested this on Windows using supplied regression tests - tests 2 and 3 both fail.
by , 17 years ago
Attachment: | safe_filename_gen.1.2.diff added |
---|
Full patch+tests with better version of test 2
comment:5 by , 17 years ago
The failure in test 2 turned out to be a problem in the test itself - it wasn't using os.path.normpath to normalise a hard-coded file path. I've uploaded a new version of the full patch with this corrected.
However, test 3 - comparing the uploaded data with the original file - still fails. It seems to be corrupting the image during upload. I checked this with the non-patched SVN, and the test passes there, so there is something going on with this patch that's causing corruption on Windows.
follow-up: 7 comment:6 by , 17 years ago
If you skip tearDown()
and manually inspect the files, do they the original jpeg (ie. is it just weird tests)?
comment:7 by , 17 years ago
I did try that - the saved file is definitely corrupt. With the default jpeg, you get the top two-thirds of the picture OK but then it starts looking very odd. I've tried it multiple times and the same corruption happens each time. I'll upload the corrupted jpg here.
I also tried it with other image files and the corruption is even worse.
by , 17 years ago
Attachment: | testfg0.jpg added |
---|
Test jpeg file showing corruption incurred during save.
comment:8 by , 17 years ago
Maybe it's doing something in text mode, even thought its set to use binary. I'll do some more experiments when I get to work tomorrow.
comment:9 by , 17 years ago
Keywords: | fs-rf added |
---|
comment:10 by , 17 years ago
Keywords: | fs-rf-docs added; fs-rf removed |
---|
comment:11 by , 16 years ago
milestone: | → 1.0 beta |
---|
comment:12 by , 16 years ago
Since r7814 (the merge of #2070), there has been some file locking logic in save_FIELD_file
.
To me at least, it seems like the fix in #2070 is just as adequate as the patches contained in this ticket (except cross-platform).
Of course, I don't believe the patch in this ticket or #2070 fixes this problem completely.
So the question: Anybody who was able to see this problem before, can you now? (post r7814)
Cheers,
Mike
comment:13 by , 16 years ago
Okay, so I'm not convinced this is really fixed by the #2070 changes, but it is very hard to fix due to the increased separation of responsibilities that change introduced. Leaving for "later" for the time being, but we should look at this again.
comment:14 by , 16 years ago
I believe the race condition still exists after the file storage refactoring;the attached t.py demonstrates it. If two threads simultaneously try to create a file, they will both create the same file, and the last writer wins. If the sleep(5) is activated between in the test, then the second writer will see that the file already exists, and create the file under a different name.
The current locking in the code doesn't really help - it only means that the content of the two different files will not interleave, but instead, that one fully overwrites the other.
What should happen instead is that the open flag for exclusive open is used, which causes failure to open if the file is already present. In that case, Storage.save should go back and ask for a different available name, as the first name has proven not to be available.
comment:15 by , 16 years ago
Resolution: | → fixed |
---|---|
Status: | assigned → closed |
Attached patch (
safe_filename_gen.1.diff
) uses the standardopen(path, O_CREAT | O_EXCL)
pattern.Since the tests in
bug639
had perfectly good models and files, I renamed it to file_field and added tests into there. A patch tobase.py
is available insafe_filename_gen__modelsonly.1.diff
.