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

Fix segmentation fault for empty output file names. #8606

Closed
wants to merge 5 commits into from
Closed

Fix segmentation fault for empty output file names. #8606

wants to merge 5 commits into from

Conversation

iblanes
Copy link

@iblanes iblanes commented Mar 18, 2022

Curl crashes with segmentation fault when output is an empty string and --create-dirs is used.

$ curl --create-dirs 'http://kernel.org' -o ""
Segmentation fault

The crash occurs because function glob_match_url sets *result to NULL when called with filename = "" (curlx_dyn_ptr returns NULL for an empty buffer) in function single_transfer. Hence, per->outfile is changed from "" to NULL.

Proposed patch ensures that Curl_dyn_ptr sets *result to at least an empty string.

Function glob_match_url set *result to NULL when called with filename = "", producing an indirect NULL pointer dereference.
bagder added a commit that referenced this issue Mar 18, 2022
bagder added a commit that referenced this issue Mar 18, 2022
@bagder
Copy link
Member

@bagder bagder commented Mar 18, 2022

That still makes curl behave weirdly and delay the error. Isn't it better to fail like #8607 ?

@iblanes
Copy link
Author

@iblanes iblanes commented Mar 18, 2022

Wow, that was a fast response :-).

My reasoning was to make the function to be consistent (get a string => return a string), but you are right that curl's behavior is still inconsistent overall with what happens when an empty string is given as output.

I guess that the underlying question is what should curl do when it gets an empty string as output file name. I would argue that it should do the same as the rest of the common command line tools and treat that as the input to open and fail as in any other invalid input to open. I.e.,

$ tee ""
tee: '': No such file or directory

But this creates uninformative error messages...

Unfortunately, there are more nuanced behaviors related to this. At some point curls seems to think that an empty output file name is equivalent to the standard output. Here curl seems to think that "" means <stdout> to later fail to open it:

$ curl --no-progress-meter 'http://{,}kernel.org' -o ""

[1/2]: http://kernel.org --> <stdout>
Warning: Remote filename has no length!
curl: (23) Failure writing output to destination

[2/2]: http://kernel.org --> <stdout>
Warning: Remote filename has no length!
curl: (23) Failure writing output to destination

Here curl produces the same empty string for one of the subtitutions and reports an empty file (no <stdout> now):

$ curl --no-progress-meter 'http://{a,}kernel.org' -o "#1"

[1/2]: http://akernel.org --> a

[2/2]: http://kernel.org --> 
Warning: Remote filename has no length!
curl: (23) Failure writing output to destination

If you think that it would be helpful (and it is needed), perhaps I may be able to contribute a patch tackling the whole thing, but I would need some directions on what do you want curl to do in this case (what -o "" means).

@bagder
Copy link
Member

@bagder bagder commented Mar 18, 2022

It struck me that my check was also done a bit too late.

I think we should consider -o "" to be wrong and an error. We use -o - as a magic shortcut to make it stdout.

Then we can probably check for a blank file name (and error out if so) already here:

GetStr(&url->outfile, nextarg);

@iblanes
Copy link
Author

@iblanes iblanes commented Mar 22, 2022

Thank you for the directions. Here is a new shot at it. This time it is a bit more comprehensive:

  1. It reports the invalid output file as you indicated in your previous comment.
  2. It ensures that glob_match_url always returns a string (even if empty) when it does not fail. Currently, glob_match_url may return NULL to indicate an empty string, but also may return an empty string. The function is just used once and it does not change much, but it seems more consistent.
  3. It adds an error check for a glob producing an empty string and reports an informative error message.
  4. It slightly modifies the documentation for Curl_dyn_ptr and Curl_dyn_uptr.

I have also followed the output file name from command line to glob_match_url and afterwards to check whether it is possible that it silently becomes an empty string again, but it seems that this is not the case (tool_create_output_file takes care of telling the user when the output file name is of zero length).

The three previous examples are now fixed:

$ curl --create-dirs 'http://kernel.org' -o ""
Warning: output file name has no length
curl: option -o: is badly used here
curl: try 'curl --help' or 'curl --manual' for more information
$ curl --no-progress-meter 'http://{,}kernel.org' -o ""
Warning: output file name has no length
curl: option -o: is badly used here
curl: try 'curl --help' or 'curl --manual' for more information
$ curl --no-progress-meter 'http://{a,}kernel.org' -o "#1"
Warning: output glob produces empty string!
curl: (23) Failed writing received data to disk/application

I have also manually tested it a bit more, finding the following extra corner cases.

This one (the error message may be unclear):

$ curl --no-progress-meter --create-dirs 'http://{a,}kernel.org' --output-dir dir -o "#1"

[1/2]: http://akernel.org --> dir/a

[2/2]: http://kernel.org --> dir/
Warning: Failed to create the file dir/: Is a directory
curl: (23) Failure writing output to destination

is fixed into:

$ curl --no-progress-meter --create-dirs 'http://{a,}kernel.org' --output-dir dir -o "#1"
Warning: output glob produces empty string!
curl: (23) Failed writing received data to disk/application

This one (curl tries to resume directory):

$ curl --no-progress-meter --create-dirs 'http://{www.,}kernel.org' --output-dir dir -C - -o "#1"

[1/2]: http://www.kernel.org --> dir/www.
curl: Can't open 'dir/'!
curl: (23) Failed writing received data to disk/application

is fixed into:

$ curl --no-progress-meter --create-dirs 'http://{www.,}kernel.org' --output-dir dir -C - -o "#1"
Warning: output glob produces empty string!
curl: (23) Failed writing received data to disk/application

And this one (curl prints a null pointer):

$ curl --no-progress-meter '{www.,}kernel.org' --output-dir dir -C - -o ""

[1/2]: www.kernel.org --> dir/(nil)

[2/2]: kernel.org --> dir/(nil)

is fixed into:

$ curl --no-progress-meter '{www.,}kernel.org' --output-dir dir -C - -o ""
Warning: output file name has no length
curl: option -o: is badly used here
curl: try 'curl --help' or 'curl --manual' for more information

Let me know if this is in line with what you had in mind.

bagder added a commit that referenced this issue Mar 24, 2022
Previously it would return a pointer if the buffer previously had
contents. This makes the code follow the documentation.

Added test case 1614 to verify this behavior.

Spotted-by: iblanes
Ref: #8606
docs/DYNBUF.md Show resolved Hide resolved
@bagder
Copy link
Member

@bagder bagder commented Mar 28, 2022

Thanks!

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.

None yet

2 participants