OPCFoundation / UA-.NETStandard

OPC Unified Architecture .NET Standard
Other
1.94k stars 942 forks source link

[Client] Time calculations using DateTime are susceptible to produce erroneous values #2546

Closed bhnaphade closed 3 months ago

bhnaphade commented 7 months ago

Proposed changes

Time calculations using DateTime are susceptible to produce erroneous values when System Time is changed during the calculation process. Computing time should be done with a monotonic clock such as existing HiResClock.

Related Issues

Types of changes

What types of changes does your code introduce? Put an x in the boxes that apply. You can also fill these out after creating the PR.

Checklist

Put an x in the boxes that apply. You can also fill these out after creating the PR. If you're unsure about any of them, don't hesitate to ask. We're here to help! This is simply a reminder of what we are going to look for before merging your code.

Further comments

If this is a relatively large or complex change, kick off the discussion by explaining why you chose the solution you did and what alternatives you considered, etc...

codecov[bot] commented 7 months ago

Codecov Report

Attention: Patch coverage is 75.00000% with 2 lines in your changes are missing coverage. Please review.

Project coverage is 54.65%. Comparing base (60689c9) to head (484691e).

Files Patch % Lines
Libraries/Opc.Ua.Client/Session.cs 60.00% 2 Missing :warning:
Additional details and impacted files ```diff @@ Coverage Diff @@ ## master #2546 +/- ## ========================================== + Coverage 54.62% 54.65% +0.02% ========================================== Files 342 342 Lines 65082 65081 -1 Branches 13350 13350 ========================================== + Hits 35553 35571 +18 + Misses 25661 25646 -15 + Partials 3868 3864 -4 ```

:umbrella: View full report in Codecov by Sentry.
:loudspeaker: Have feedback on the report? Share it here.

OPCLabs commented 5 months ago

I am concerned about the use of HiResClock.TickCount. It will wrap over in less than 25 days after the computer is started and cause problems, and there is no code that checks for it. Perhaps HiResClock.TickCount64 was intended. I also suggest to mark HiResClock.TickCount with [Obsolete] with appropriate message, which would force the developers to reconsider every place where it is used.

mrsuciu commented 5 months ago

@OPCLabs

I understand the concern, but are time intervals involved in the calculations greater than 24.9 days empirically possible? Do you see any such situations? No "wrap-around" checks are needed since under C#, integer subtraction operations are handled gracefully also with "wrap-around" values that exceed the bounds of the integer data type and produce the correct result.

OPCLabs commented 5 months ago

@mrsuciu, what you wrote about "automatic" correct handling of wrap-around would be true if you stayed within the same integer type (Int32, here). The typical operation is subtracting some old time from the new time, and comparing that against some timeout value. With Environment.TickCount, which returns Int32, this operation will work well (even with wrap-around), if it is made as Int32 subtraction. But, this is not the case in the code I am concerned about. Here, m_lastKeepAliveTime is beig subtracted effectively from what is Environment.TickCount, and the m_lastKeepAliveTime is declared as long (Int64), therefore the subtraction will be performed as Int64 subtraction, and it won't give the expected result with wrap-around, I think.

mrsuciu commented 5 months ago

@OPCLabs

I see what you mean now, but that problem was signaled previously also by @mregen here: https://github.com/OPCFoundation/UA-.NETStandard/pull/2546#discussion_r1579154481 so I thought you were generally concerned about the use of HiResClock.TickCount as opposed to HiResClock.TickCount64 having also the implication of not checking the "wrap-around".

OPCLabs commented 5 months ago

@mrsuciu yes, maybe I should have formulated it better originally, so the "mismatch" between Int32 and Int64 is the problem, and it can be solved in two ways, going full Int32 or full Int64. I did not notice that @mregen had already spotted it. So if it being addressed, either way, I am fine.

mregen commented 5 months ago

not quite ready