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

Raise if passing null Pointer to String #9653

Merged
merged 5 commits into from
Aug 31, 2020
Merged

Conversation

jgaskins
Copy link
Contributor

@jgaskins jgaskins commented Aug 3, 2020

This is probably preferable to a SEGV.

Source discussion

I’ll add some specs for it, but I just wrote this in the browser on an iPad for the moment and while I’m thinking about it.

Also, I don’t know if it makes sense to create a new exception class for this particular bug, because Exception feels too broad.

@jhass
Copy link
Member

jhass commented Aug 3, 2020

If we want this it probably should be an ArgumentError.

"Cannot generate" feels weird, this isn't really generating anything.

Copy link
Member

@jhass jhass left a comment

Choose a reason for hiding this comment

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

Let's have some simple specs for this

end

it "returns an empty string when creating from a null pointer with size 0" do
String.new(Pointer(UInt8).null, 0).should eq ""
Copy link
Member

Choose a reason for hiding this comment

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

I'm fine with this behavior, but it might warrant some discussion.

Copy link
Contributor

Choose a reason for hiding this comment

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

@jhass Why? This PR doesn't change the way it works right now.

Copy link
Member

Choose a reason for hiding this comment

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

Previously null pointers were just not considered by the implementation. That it doesn't change behavior for this case is more of an implementation detail than design. This considers how to deal with null pointers from a design perspective, so the consideration to change this is within scope in my opinion.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm surprised there were no specs about pointers with 0 length because it seemed to be an intentional choice in the code.

Copy link
Member

Choose a reason for hiding this comment

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

I think I would prefer String.new(Pointer(UInt8).null, 0) to raise. As raised by @jhass null pointers here were probably not considered before. It think it will also be more consistent with the behavior of String.new(Pointer(UInt8).null).

The optimization that lead to the bytesize == 0 check is about reusing the empty string literal embeded in the binary.

@jgaskins I know it's been a while, let me know if you prefer to hand of these changes.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@bcardiff I'm happy to turn it over to someone that understands the implications better than I do. I mainly wanted to see if there was a better way of handling char *s = 0 from a C function since someone I was chatting with about Crystal ran into it.

Copy link
Member

Choose a reason for hiding this comment

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

@jhass @asterite Do you agree then to make String.new(Pointer(UInt8).null, 0) raise? If so, I can finish this up.

Thanks @jgaskins . The intention was to fail in a more controlled way in case of char *s = 0, right? Is not that that person was expecting s to be an empty string, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Whichever makes the most sense. I think either case is better than a segfault. I originally had the same perspective @asterite did in this comment, but your response to it makes a lot of sense. And since any time we're dealing with pointers, we're generally also dealing with C code, I feel like making the distinction between an empty string and a null pointer seems like a reasonable requirement.

Copy link
Member

Choose a reason for hiding this comment

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

I feel okay either way. We can see if somebody runs into trouble with the changed behavior. It seems reasonable to me that something would return a null pointer and a zero length in an error or "not found" case, but have no example at hand to back it up.

@jgaskins
Copy link
Contributor Author

jgaskins commented Aug 5, 2020

The hilarious thing about the build is that I seem to have somehow created a CircleCI build on my own fork of the repo, which doesn't have macOS builds enabled, so the macOS build failed. I have no idea how to cancel it. 😂

end

it "returns an empty string when creating from a null pointer with size 0" do
String.new(Pointer(UInt8).null, 0).should eq ""
Copy link
Member

Choose a reason for hiding this comment

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

I think I would prefer String.new(Pointer(UInt8).null, 0) to raise. As raised by @jhass null pointers here were probably not considered before. It think it will also be more consistent with the behavior of String.new(Pointer(UInt8).null).

The optimization that lead to the bytesize == 0 check is about reusing the empty string literal embeded in the binary.

@jgaskins I know it's been a while, let me know if you prefer to hand of these changes.

@asterite
Copy link
Member

I think raising on null pointer with length zero might be a breaking change and actually annoying. For example you ask a string from a C library. It turns out it's the empty string. The only way one can represent that in C is with a null pointer. Maybe it then returns the length of the string, which is zero. You pass those to String.new and get an exception. And worse, you have no easy way to fix it. You have to special case it.

There's nothing dangerous about passing a null pointer with a size zero. I think we should keep that behavior.

@bcardiff
Copy link
Member

The only way one can represent that in C is with a null pointer

I'm confused. Usually functions with char* assume valid pointers. Behavior on null is undefined.

Do you recall C functions that return null as an empty string? Why couldn't they return a pointer to a \0 char?

@asterite
Copy link
Member

Oh, I think you are right. For the empty string it will be a pointer to the null character, not a null pointer.

@bcardiff bcardiff added this to the 1.0.0 milestone Aug 28, 2020
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.

5 participants