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 ber scanf/printf. #51205

Merged
merged 5 commits into from
Apr 28, 2021
Merged

Fix ber scanf/printf. #51205

merged 5 commits into from
Apr 28, 2021

Conversation

sandreenko
Copy link
Contributor

@sandreenko sandreenko commented Apr 14, 2021

That is an alternative solution for #49105, instead of #50540.

OSX arm64 has a special calling convention for varargs so we can't rely on the old mechanism when we declared methods as non-varargs but the arguments went to the registers and stack slots that were expected by a native vararg callee.

For completeness, I do the change on all platforms so we don't rely on this behavior that is not guaranteed to work on new platforms/os in the future.

Use the vararg calling convention on Windows and non-vararg methods that are provided by OpenLDAP on Unix.

@sandreenko sandreenko force-pushed the fixBerScanfPrintf branch 2 times, most recently from 4ac15ed to 91fce13 Compare April 14, 2021 02:28
@sandreenko sandreenko requested a review from joperezr April 14, 2021 07:56
@sandreenko sandreenko marked this pull request as ready for review April 14, 2021 07:57
@sandreenko
Copy link
Contributor Author

sandreenko commented Apr 14, 2021

@joperezr your idea of using OpenLdap specific functions looks promising, it is much easier to do then the native shim, however, I can't find how to encode ber_printf(berElem, 't'), looks like there is no ber_put_tag or something.
that is how it works in ldap:
https://clickhouse.tech/codebrowser/html_report/ClickHouse/contrib/openldap/libraries/liblber/encode.c.html#586
we change ber->ber_tag = va_arg( ap, ber_tag_t ); and return.

If we want to stick to this solution we can do a few things:

  1. change ber->ber_tag on C# side - sounds bad because we will have a dependency on BerElement layout;

  2. use "ber_tag_t" argument that "ber_put" functions have
    https://clickhouse.tech/codebrowser/html_report/ClickHouse/contrib/openldap/libraries/liblber/encode.c.html#ber_put_int
    so we will save the tag value when we see it on c# side and then pass it to the native function - it would introduce a difference between windows and unix implementation, Windows does not have "get/put" methods so we have to stick to "scanf/printf" there.
    The change will be here:


    we will start to save "tag" value in a C# variable and pass it to all calls, Windows will ignore it, Unix will use it.

  3. say that the "t" format is unsupported for printf on Unix, it is not currently supported for scanf, so not a big loss?

I will try to code 2. tomorrow, if we don't extend API and don't allow the users to work with the BerElement directly it should work (otherwise we can lose a tag).

@joperezr
Copy link
Member

Thank you so much for trying this other approach. I agree with your plan of attempting 2 and if that doesn't work then just going with 3 instead. To be honest we don't really use all of the encoding support fully as we mostly only have it to support the basic scenarios that the communication with ldap requires. We have good unit test coverage on the BerPal, and we have some tests that need to be rujn manually against an ldap server to ensure that the overall connection and manipulation of ldap still works as expected, so I can give you more details on how to run that too.

@sandreenko
Copy link
Contributor Author

/azp run runtime-libraries-coreclr outerloop, runtime-libraries-coreclr outerloop-osx

@azure-pipelines
Copy link

Azure Pipelines successfully started running 2 pipeline(s).

@sandreenko
Copy link
Contributor Author

/azp run runtime-libraries-coreclr outerloop, runtime-libraries-coreclr outerloop-osx

@azure-pipelines
Copy link

Azure Pipelines successfully started running 2 pipeline(s).

@sandreenko
Copy link
Contributor Author

The failures are due to #51346, I think this PR is ready for review.
Note I have made many changes in the managed implementation of BER that I am not very familiar with so I was mostly relying on our test coverage. Would be nice if somebody who is familiar with BER could review it.

PTAL @joperezr @ericstj , cc @jkotas , @janvorli

@sandreenko
Copy link
Contributor Author

ping @joperezr @ericstj


public static int ber_printf_tag(SafeBerHandle berElement, string format, int tag)
{
return ber_default_successful_return_code;
Copy link
Member

Choose a reason for hiding this comment

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

Should we add a comment here saying why we are not supporting this? Or I wonder if for this scenario we should falk back to use the varargs func instead so that it works as expected on platforms where they are supported?

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 will add a comment:
// Ber Linux tags are passed with the values that they affect, so this function does nothing.

public static extern int ber_scanf_bitstring(SafeBerHandle berElement, string format, ref IntPtr value, ref int bitLength);
public static int ber_printf_berarray(SafeBerHandle berElement, string format, IntPtr value, int tag)
{
Debug.Assert(format == "v" || format == "V");
Copy link
Member

Choose a reason for hiding this comment

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

Same here, could we just fall back to call the old printf in this case? I know it means that this won't work in the new Macs, but that means we won't regress existing runtimes in case support is added in the future.

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 have supported 'v' on Linux (and changed the Windows version to be consistent) by converting one 'v' call to several 'o' calls:

it does not work for 'V' because I have not found an API that takes 'BerVal'.
The definition is:

V

Several octet strings. A null-terminated array of struct berval *'s is supplied. Note that a construct like '{V}' is required to get an actual SEQUENCE OF octet strings.

and there is no API that takes a single A null-terminated struct berval *'s

so the only thing that regressed is 'V' encoding for Linux. 't' (encoding, decoding), 'v'(encoding) are supported'; 'V' and 'v' decoding were not supported before these changes.

public static extern int ber_scanf_ptr(SafeBerHandle berElement, string format, ref IntPtr value);
public static int ber_scanf_multibytearray(SafeBerHandle berElement, string format, ref IntPtr value)
{
Debug.Assert(format == "v" || format == "V");
Copy link
Member

Choose a reason for hiding this comment

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

Same here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Decode for vv and VV was not supported previously so we don't regress anything here:

if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) // vv and VV formats are not supported yet in Linux

Copy link
Member

Choose a reason for hiding this comment

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

yeah I understand that, but what I mean is imagine that in a future version of libldap (if it ever ships) they add support for vv or VV, then the old code would have just worked as the scanf calls would just light up and start working; whereas if we keep it the way you have it we would have to basically make the change manually. TBH I doubt libldap will ship a new future major version with this support so I'm fine keeping it as you have it but just wanted to raise the comment to get your 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.

Does this comment means that "vv" and "VV" are not supported in System.DirectoryServices.Protocols or in OpenLDAP? I thought it was about "System.DirectoryServices.Protocols". I guess it was not supported exactly because vararg calling convention for passing such struct was different so we could not call it even on x64 Unix.


valueCount++;
}
else if (fmt == 't')
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 sure I'm missing something obvious but I'm not understanding why we mvoed the t here. even when unsupported seems to me we are doing sort of the same than we were doing before right?

Copy link
Contributor Author

@sandreenko sandreenko Apr 19, 2021

Choose a reason for hiding this comment

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

't' means tag and it applies to the next thing in the string, there is no API that does this, like "ber_put_arg" or something, so I introduced a mechanism on C# side that saves the tag when we see it:

                    tag = (int)value[valueCount];
                    tagIsSet = true;

and then pass it to the next value, so the tags are kept working on Linux.

@joperezr
Copy link
Member

just as an overall comment, things look very good here and I'm happy to see that all of our ber unit tests are passing, but it would be good to run all of our scenario tests against an actual LDAP server before merging just in case.

@sandreenko
Copy link
Contributor Author

but it would be good to run all of our scenario tests against an actual LDAP server before merging just in case.

How could I do this?

@sandreenko
Copy link
Contributor Author

ping @joperezr

Copy link
Member

@joperezr joperezr left a comment

Choose a reason for hiding this comment

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

I have ran the manual tests against Active Directory server in both Windows and Linux and they both passed just fine so I think we can go ahead and merge this.

@joperezr
Copy link
Member

Thanks a lot for the refactoring work @sandreenko!

@sandreenko sandreenko merged commit f664348 into dotnet:main Apr 28, 2021
@sandreenko
Copy link
Contributor Author

Thanks @joperezr for your help with it!

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants