Django

Code

Ticket #4948 (closed: fixed)

Opened 1 year ago

Last modified 4 months ago

Saving FileField files is not thread-safe

Reported by: anonymous Assigned to: rcoup
Milestone: 1.0 beta Component: Core framework
Version: SVN Keywords: fs-rf-docs
Cc: Triage Stage: Accepted
Has patch: 0 Needs documentation: 0
Needs tests: 0 Patch needs improvement: 1

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

safe_filename_gen__modelsonly.1.diff (1.7 kB) - added by rcoup on 09/14/07 20:33:07.
safe_filename_gen.1.diff (6.5 kB) - added by rcoup on 09/14/07 20:36:39.
Been doing this too long…
safe_filename_gen.1.2.diff (6.5 kB) - added by danielr on 09/15/07 07:58:51.
Full patch+tests with better version of test 2
testfg0.jpg (1.7 kB) - added by danielr on 09/15/07 17:12:41.
Test jpeg file showing corruption incurred during save.
t.py (0.5 kB) - added by loewis on 08/09/08 10:24:42.
Demonstration of race condition

Change History

07/21/07 09:02:38 changed by Simon G. <dev@simon.net.nz>

  • needs_better_patch changed.
  • stage changed from Unreviewed to Accepted.
  • needs_tests changed.
  • needs_docs changed.

09/14/07 20:32:35 changed by rcoup

  • owner changed from nobody to rcoup.
  • status changed from new to assigned.

Attached patch (safe_filename_gen.1.diff) uses the standard open(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 to base.py is available in safe_filename_gen__modelsonly.1.diff.

09/14/07 20:33:07 changed by rcoup

  • attachment safe_filename_gen__modelsonly.1.diff added.

09/14/07 20:36:39 changed by rcoup

  • attachment safe_filename_gen.1.diff added.

Been doing this too long...

09/14/07 20:59:04 changed by rcoup

Malcolm pointed out this needs confirmation on Windows wrt. O_CREAT & O_EXCL

09/15/07 07:11:43 changed by danielr

  • needs_better_patch set to 1.

Tested this on Windows using supplied regression tests - tests 2 and 3 both fail.

09/15/07 07:58:51 changed by danielr

  • attachment safe_filename_gen.1.2.diff added.

Full patch+tests with better version of test 2

09/15/07 08:02:14 changed by danielr

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 ) 09/15/07 08:36:48 changed by rcoup

If you skip tearDown() and manually inspect the files, do they the original jpeg (ie. is it just weird tests)?

(in reply to: ↑ 6 ) 09/15/07 17:11:42 changed by danielr

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.

09/15/07 17:12:41 changed by danielr

  • attachment testfg0.jpg added.

Test jpeg file showing corruption incurred during save.

09/15/07 20:22:32 changed by rcoup

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.

12/11/07 13:28:35 changed by Gulopine

  • keywords set to fs-rf.

12/16/07 21:15:55 changed by Gulopine

  • keywords changed from fs-rf to fs-rf-docs.

06/16/08 13:29:56 changed by Gulopine

  • milestone set to 1.0 beta.

07/04/08 00:14:03 changed by axiak

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

08/08/08 18:00:40 changed by mtredinnick

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.

08/09/08 10:24:42 changed by loewis

  • attachment t.py added.

Demonstration of race condition

08/09/08 10:30:29 changed by loewis

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.

08/11/08 11:51:19 changed by jacob

  • status changed from assigned to closed.
  • resolution set to fixed.

(In [8306]) Fixed #4948, a race condition in file saving. Thanks to Martin von Löwis, who diagnosed the problem and pointed the way to a fix.


Add/Change #4948 (Saving FileField files is not thread-safe)




Change Properties
Action