Skip to content

Comments

Simplify the function OPENSSL_isservice#4096

Closed
xiaoyinl wants to merge 4 commits intoopenssl:masterfrom
xiaoyinl:isservice
Closed

Simplify the function OPENSSL_isservice#4096
xiaoyinl wants to merge 4 commits intoopenssl:masterfrom
xiaoyinl:isservice

Conversation

@xiaoyinl
Copy link
Contributor

@xiaoyinl xiaoyinl commented Aug 5, 2017

I am not sure why a union is needed here. It seems to me a simple static function pointer variable is OK.
Also added a comment to clarify why we want to call the exported function _OPENSSL_isservice.

I am not sure why a union is needed here. It seems to me a simple static function pointer variable is OK.
Also added a comment to clarify why we want to call the exported function _OPENSSL_isservice.
@dot-asm
Copy link
Contributor

dot-asm commented Aug 6, 2017

Union here follows a common pattern, which in turn is a recognition of the fact that language standard doesn't guarantee meaningful outcome of cast between pointer to data and pointer to function, or vice versa. Or rather that it's specified to be implementation-defined. Even though direct, union-less cast should work as expected with Win32 compilers, it doesn't actually prohibit them to issue a warning. And even though they don't issue warning, it's not inappropriate to adhere to pattern that is guaranteed to be warning-free.

.. by using a Boolean variable to flag whether f_isservice has a cached value.
@xiaoyinl
Copy link
Contributor Author

xiaoyinl commented Aug 6, 2017

@dot-asm Thank you very much for your detailed explanation! Now I see why a union is used here. I pushed another commit. This time I use a flag variable to track whether we have a cached result in the function pointer, which I think is warning-free and probably easier to understand.

Copy link
Contributor

@richsalz richsalz left a comment

Choose a reason for hiding this comment

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

two nits.

static FARPROC f_isservice = NULL;
static int is_first_time = 1;

/* Allow application to override OPENSSL_isservice by exporting
Copy link
Contributor

Choose a reason for hiding this comment

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

style is that the "/*" appears on a line by itself.


if (_OPENSSL_isservice.p == NULL) {
static FARPROC f_isservice = NULL;
static int is_first_time = 1;
Copy link
Contributor

Choose a reason for hiding this comment

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

please put the static variables first.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Both are fixed. Thanks!

Copy link
Contributor

Choose a reason for hiding this comment

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

that was quick, thanks :)

@dot-asm
Copy link
Contributor

dot-asm commented Aug 6, 2017

Allow me to be more explicit. It's firm -1 from me. Suggested alternative is explicitly thread unsafe. One can argue that original is not exactly thread-safe in conventional sense, but it works in practice because value returned by GetProcAddress is constant. Worse that can currently happen is that GetProcAddress is executed twice, while worse that can happen in this merge result request is crash.

@xiaoyinl
Copy link
Contributor Author

xiaoyinl commented Aug 6, 2017

I totally overlooked thread safety. Sorry! Now I move is_first_time = 0; after GetProcAddress, so now f_isservice is guaranteed to have the right value when the second thread bypasses GetProcAddress.

@xiaoyinl
Copy link
Contributor Author

xiaoyinl commented Aug 6, 2017

Maybe I am wrong again, but now I feel my latest proposed code is slightly better in thread safety than the original code. For the original one, there is a very tiny possibility that it causes a NULL dereference. It can occur if after thread A sets p to -1, thread B sets f to 0 at Line _OPENSSL_isservice.f = GetProcAddress(...). Then before thread B sets the union value back to -1, thread A executes (*_OPENSSL_isservice.f) ();, which results in null dereference.

My proposed code doesn't have this issue because f_isservice only has two possible values: NULL or a valid address, and it's impossible to change back to NULL after it's set to a valid address.

@dot-asm
Copy link
Contributor

dot-asm commented Aug 14, 2017

Right. Compiler can still generate right code, but we may not assume that it does. However! For same reason we may not assume that compiler compiles code that is using two variables, one being kind of synchronization flag, correctly. In other words, having single "self-synchronizing" variable is the only viable approach. Sorry about delay...

@xiaoyinl
Copy link
Contributor Author

I see. Thanks for explanation again!

@xiaoyinl xiaoyinl closed this Aug 14, 2017
@xiaoyinl xiaoyinl deleted the isservice branch August 14, 2017 20:50
@dot-asm
Copy link
Contributor

dot-asm commented Aug 16, 2017

But you don't want to fix it? I mean there formally is problem, just two variables is not right approach...

@xiaoyinl
Copy link
Contributor Author

But I can't think of a better way to implement it with one variable.

@kroeckx kroeckx mentioned this pull request Apr 1, 2020
1 task
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.

3 participants