Skip to content

Add asn1_time_to_tm function and check days in month#3905

Closed
InfoHunter wants to merge 1 commit intoopenssl:masterfrom
InfoHunter:refactor-asn1-time
Closed

Add asn1_time_to_tm function and check days in month#3905
InfoHunter wants to merge 1 commit intoopenssl:masterfrom
InfoHunter:refactor-asn1-time

Conversation

@InfoHunter
Copy link
Copy Markdown
Member

@InfoHunter InfoHunter commented Jul 11, 2017

Based on discussion in PR #3566 (comment). Reduce duplicated code in original
asn1_utctime_to_tm and asn1_generalizedtime_to_tm, and introduce a new
internal function asn1_time_to_tm. This function also checks if the days
in the input time string is valid or not for the corresponding month. (leap year is considered)

Checklist
  • tests are added or updated

@InfoHunter
Copy link
Copy Markdown
Member Author

@kroeckx might be interested in this.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Is this right? I mean provided that it still can return error below. I mean wouldn't it be more appropriate to modify destination data only if you return success?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

You're right. This logic follows the original asn1_utctime_to_tm function and in that function there is no tmp variable to 'cache' the struct tm (for later usage of adjusting offsets). Since tmp is introduced in this new function, it could be more 'neat' to defer this thing before return success.

@InfoHunter InfoHunter force-pushed the refactor-asn1-time branch from fc6ca26 to cee46e0 Compare July 13, 2017 15:36
@InfoHunter
Copy link
Copy Markdown
Member Author

Updated. And also changed the logic of checking days in month.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Do you mind either adding parenthesis around first expression, or splitting these two declarations to separate lines. Note that second one is preferred according to style guide. And while you are at it throw in empty line after declaration.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

OK, I prefer to split it into two lines.

Copy link
Copy Markdown
Contributor

@dot-asm dot-asm Jul 13, 2017

Choose a reason for hiding this comment

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

One can ask same question whether or not it's appropriate to modify *tm if asn1_time_to_tm() returns error. Why not simply let asn1_time_to_tm() decide? There is certain ambiguity in documentation, it says "Only the B<tm_sec>, B<tm_min>, B<tm_hour>, B<tm_mday>, B<tm_mon> and B<tm_year> fields are updated." It sounds like that if you pass a tm if it won't touch fields other than listed. This doesn't seem to be true. I mean it wasn't true to start with... Hmmm...

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

But if documentation's intention was to say that it returns only these fields [zeroing all others], then one can probably argue that one should do just that. That is call asn1_time_to_tm(&tmp,...) and copy listed fields...

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Ah! Disregard comment implied suggestion to harmonize with documentation by copying listed fields. Because those are only fields set by underlying subroutine. Sorry about confusion. So there is only one question, about letting asn1_time_to_tm() to decide if *tm is to be modified. There is memset in asn1_time_to_tm(), so it will be equivalent...

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Though one can wonder if documentation should be refined, e.g. by stating that only listed fields are set to meaningful values.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

So the problem is how should the ASN1_TIME_to_tm be designed. There might be 2 choices:

  1. set the listed (in doc) fields of tm to proper values, and zero all others.
  2. set the listed fields of tm to proper values, and leave other fields as-is.

For both choices, it's only applied if the 'TIME' s is parsed successfully, otherwise leave tm untouched.

I prefer the first choice, and I guess this is what ASN1_TIME_to_tm intends to be. If a user wants to just fill the listed fields, he/she could call something like ASN1_TIME_to_tm(s, &tmp) and copy the fields of tmp to other tm/places by his/her own. That's not the scope covered by this API.

So the memset stuff in ASN1_TIME_to_tm should be removed and documentation of ASN1_TIME_to_tm should be modified to match this behavior either.

And also, in this scenario, if a NULL tm is passed to ASN1_TIME_to_tm and finally is passed to asn1_time_to_tm, the ASN1_TIME_to_tm will perform in a 'format-checking-only' mode, which is similar to what ASN1_TIME_set_string does. This feature should also be documented, of course.

Makes sense to what you suggested?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

If you examine what are the remaining fields of the struct tm, i.e. those not listed, you'll see that they are tm_wday, tm_yday and tm_isdst. Confusion arose from the assumption that that there are more fields, or rather such that would be considered as worth preserving. Once again, wording "only fields x, y, z, are updated" kind of implies that caller is entitled to expect that other fields are preserved and worth preserving. But after closer look it's obvious that it's essentially unnatural to expect this. It's only natural that non-listed fields are zeroed. And that's what happens, and even without referred memset, asn1_time_to_tm does it in either case.

To summarize, bottom line is that it's sufficient to omit memset, and in additoin to that I'd argue in favour of rewording documentation to avoid the misconception.

@InfoHunter InfoHunter force-pushed the refactor-asn1-time branch from cee46e0 to 1378d05 Compare July 14, 2017 19:09
@InfoHunter
Copy link
Copy Markdown
Member Author

Remove memset in ASN1_TIME_to_tm and update documentation.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This doesn't belong here. As already discussed *tm modification is done only at the very end and if there were no errors.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Ahh, this is definitely a bug, tmp should be updated instead of tm. And it seems current test cases don't cover this...

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Good catch!

Copy link
Copy Markdown
Member Author

@InfoHunter InfoHunter Jul 15, 2017

Choose a reason for hiding this comment

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

Ahh, this is definitely a bug, tmp should be updated instead of tm. And it seems current test cases don't cover this...

Hmm, tmp is also not needed to be set to 0, since we already have memseted all tmp to 0 previously. So this piece of code will not cause problems, but it's indeed unnecessary to exist. For test cases, I have checked it again, the no 'SS' (seconds) situation is already covered, so that's great : )

[edited: quote previous comments to make the statements more clear]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Can one eliminate this condition? I mean would it be inappropriate to make the adjustment if tm is NULL?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

And if it's inappropriate, tmp != NULL is considered to be more appropriate.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This might not be a problem. The idea is, tmp is always there and is filled with data no matter if tm is NULL or not. So if tm is NULL, there is no need to update tm by tmp and tmp will silently be ignored. So if tm is NULL, no need to adjust tmp neither.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

But there is a coding style problem. if (tm) to if (tm != NULL) ...

Copy link
Copy Markdown
Member Author

@InfoHunter InfoHunter Jul 15, 2017

Choose a reason for hiding this comment

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

Also, I guess you would ask that, why other fields of tmp are not updated based on the presence of tm in the above 'digital-parsing' loop. That is because the adjustment operation performed in OPENSSL_gmtime_adj is more heavy than those variables assignments, so I think put a tm != NULL check here only for adjustment makes sense.

@richsalz
Copy link
Copy Markdown
Contributor

richsalz commented Jul 15, 2017 via email

@InfoHunter InfoHunter force-pushed the refactor-asn1-time branch from 1378d05 to 711c5b4 Compare July 15, 2017 10:41
@InfoHunter
Copy link
Copy Markdown
Member Author

updated: remove useless code and fix coding style issues.

@dot-asm
Copy link
Copy Markdown
Contributor

dot-asm commented Jul 16, 2017

Admittedly I have missed prior discussion and looked at this request purely at its current face value. And it does make perfect sense to omit duplicate code.

This confusion and undocumented fields is why I was suggesting we don’t do this. Convert to time_t and let the local developer and OS handle it.

At the same time one can argue that time_t is not optimal choice either. This is because there is supported OS, *cough*-dows, where you can flip the definition with compiler flag, quite creative but nevertheless. Also, it's not inappropriate to provide sub-second granularity in some APIs. In other words we might be better off defining own 64-bit time with sub-second granularity. And once it's done, it would be only appropriate to stick to it in all cases. That's why I suggested that time_t is not optimal choice. I mean because one type is better than two.

But in either case, yes, one can wonder why is asn1_time_to_tm is promoted to exported. Can we achieve the goal (of rejection of invalid date strings, right?) without committing ourselves to another exported function? Was this discussed? This is kind of sliding away from the topic, because ASN1_TIME_to_tm was exported in independent move... And given the face value of this request it appears perfectly appropriate to approve.

@dot-asm dot-asm added branch: master Applies to master branch approval: review pending This pull request needs review by a committer labels Jul 16, 2017
@InfoHunter
Copy link
Copy Markdown
Member Author

ping @openssl for the second review.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Is it necessary to modify the mdays array here? Change the += to + and declare mdays as static const above.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Filling tm_wday and tm_yday is straightforward. Is there any reason this hasn't been done?

This isn't a request to do it.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I don't know why these 2 fields are not filled neither. Is it possible that not all struct tm has those fields on any platform?

Copy link
Copy Markdown
Member Author

@InfoHunter InfoHunter Jul 19, 2017

Choose a reason for hiding this comment

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

Besides, is it really straightforward to calculate tm_wday based on the given year-month-day info?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The structure in question is actually specified in C language standard, so that probability of the 2 fields in question not being available on some platform is virtually zero. Or at least most portability problems of this kind are attributed rather to POSIX than to C language.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The weekday is straightforward. Zeller's congruence is the magic to search for.
I'm happy to add this later as another PR, I've written this code before and would just have to adjust it to match.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

And add test cases.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

s/th/to/

Based on discussion in PR openssl#3566. Reduce duplicated code in original
asn1_utctime_to_tm and asn1_generalizedtime_to_tm, and introduce a new
internal function asn1_time_to_tm. This function also checks if the days
in the input time string is valid or not for the corresponding month.

Test cases are also added.
@InfoHunter InfoHunter force-pushed the refactor-asn1-time branch from 711c5b4 to 1cc3a8f Compare July 19, 2017 05:47
@InfoHunter
Copy link
Copy Markdown
Member Author

pushed to fix what @paulidale commented.

@dot-asm
Copy link
Copy Markdown
Contributor

dot-asm commented Jul 19, 2017

For reference, #3963, is effectively triggered by this request. I'll wait for @paulidale's verdict, then re-assess mine and commit.

Copy link
Copy Markdown
Contributor

@paulidale paulidale left a comment

Choose a reason for hiding this comment

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

Looks good here.

@kroeckx kroeckx added approval: done This pull request has the required number of approvals and removed approval: review pending This pull request needs review by a committer labels Jul 21, 2017
levitte pushed a commit that referenced this pull request Jul 23, 2017
Based on discussion in PR #3566. Reduce duplicated code in original
asn1_utctime_to_tm and asn1_generalizedtime_to_tm, and introduce a new
internal function asn1_time_to_tm. This function also checks if the days
in the input time string is valid or not for the corresponding month.

Test cases are also added.

Reviewed-by: Andy Polyakov <appro@openssl.org>
Reviewed-by: Paul Dale <paul.dale@oracle.com>
(Merged from #3905)
@paulidale
Copy link
Copy Markdown
Contributor

Merged.

@paulidale paulidale removed the approval: done This pull request has the required number of approvals label Jul 23, 2017
@paulidale paulidale mentioned this pull request Jul 23, 2017
2 tasks
@InfoHunter InfoHunter closed this Jul 24, 2017
@InfoHunter InfoHunter deleted the refactor-asn1-time branch July 24, 2017 06:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

branch: master Applies to master branch

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants