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
segfault on reusing a structure after ASN_STRUCT_FREE_CONTENTS_ONLY(). #141
Comments
I thought about changing @velichkov what do you think about this? |
The problem bites you when the OCTECT_STRING is inside a "choice" union. It doesn't matter if the pointer was zero'ed in one of the "choice" message structures, if you then re-use the message for a different message type with a different structure, it may have OCTECT_STRING elements with a different alignment in the union. This means the new message's OCTECT_STRING could have a random garbage pointer from other parts of the previous message structure in the union. Note I didn't hit this problem before 0.9.27 and I switched to native types, but I suspect I just got lucky with the alignment of the elements in the different message types using non-native types. It's not a big deal once you know that ASN_STRUCT_FREE_CONTENTS_ONLY() doesn't guarantee that the message is initialized into a sane state and you need to zero it yourself. The main niggling issue is it ALMOST initializes it into a sane state, wasting a few cycles doing so, and giving you a false impression that it does. IMHO it should either free and initialize it properly (which would maybe involve the CHOICE_free() zeroing the whole union), or don't initialize it at all. I lean towards the latter choice since it's consistent with the behavior of free(), but you probably want to make it clear either way what the behavior is. |
I see. Does it apply to
I lean towards the first choice (no pun intended :). I wonder if the whole thing should be zeroed in case of SET as well? @velichkov, your input is welcome here! @brchiu what's your opinion? If |
My bad, @dbaarda In
Could this be helpful ? |
The problem is it's not enough to zero out the pointers, you need to zero out everything, including native integers and doubles since a different message type will overlay a different struct in the union over that value, which might end up putting a pointer where that native integer or double was before. If you are really going to do this, then every *_free() for "leaf" elements must zero out it's entire contents if contents_only is true (if contents_only is false, the whole thing is going to be free'd and thus doesn't need to be wiped). Container elements like CHOICE and SET can rely on their contained elements zeroing their own contents, and just need to zero any extra supporting struct fields like count's or choice values. |
@dbaarda Got it. Again, I found the problem that not knowing the size of previous allocated memory indeed exists for *_free(), unlike Taking So the task is to have a suitable way, when we want to zero out memory allocated for Good news is : PR #93 has done this However, since @mouse07410's repository has included this #93, through merging #143 indirectly.
After a quick look into |
@brchiu I agree - it isn't that simple. I'm considering perhaps just documenting that if a. the PDU includes then using |
hi, Both, Based on @mouse07410's repository master branch, I partially implement a fix to tackle this issue in my own repository zero_out_structure_when_free_content_only. The reason I said 'partially' because only There are Review and advice are welcomed. |
I think if anything that has a union zero's out the space used by that union, then the problem will be mostly fixed. I think since you've covered the "container" types, I think you have it covered. |
When calling SET_free(), SET_OF_free(), SEQUENCE_free(), CHOICE_free() and OCTET_STRING_free() with contents_only = 1, we zero out the memory pointed by ptr for struct_size bytes. This is trying to solve Issue vlm#141.
I agree in principal, but I thought that only Also, let's continue this discussion here: |
In the meanwhile, hopefully fixed in the mouse-master. |
@mouse07410 or @brchiu, would you please post a single comment summary of the arguments and a suggestion in this patch? So far I am leaning towards fixing the _FREE/_free semantics to be non-zeroing, and creating |
@vlm, I don't mind to revoke this pr if there is better way to solve (or prevent users' wrong usage) this issue. You can just do what you think is the best on your solely discretion. Thank you for your great effort. |
@vlm I think that since the patch does fix the problem without imposing undue restriction on what a reasonable user would expect, and is fairly simple/small - it should be in. |
I was reluctant to merge mouse07410#12, because it introduces recursive memory resetting behavior when contents_only option is present. Consider the hierarchical structures. Freeing them would memset() each of the inner structures, then memset() the outer structure, and so on. This essentially resets each byte of the structure as many times as the number of layers in the hierarchy. This is wasteful and unnecessary. Therefore I introduced |
I'm not sure if this is a bug or working as intended. I didn't encounter this problem with v0.9.24, and am seeing it now with 0.9.27, but maybe I just got lucky before. I also switched from native-types to wide-types which may have contributed.
When trying to reuse a message structure after using and then "emptying" it, setting attributes using things like OCTET_STRING_fromBuf() can segfault. An example LDAP code fragment looks like this;
The problem is that OCTET_STRING_fromString() tries to free the existing matchedDN attribute before alloc/set the new one. However, the existing matchedDN attribute contains a garbage pointer left over from the previous searchResEntry message.
I realize this is pretty normal C behavior; freeing things doesn't re-initialize/zero the pointers, and it's up to the caller to do that. However, there appears to be some effort in things like OCTET_STRING_free() to leave the free'd structure in a valid initialized state, doing things like zeroing the pointer after free. Digging around other *_free() functions it looks like most others don't do this.
I think the right thing here is to adhere to C conventions and have ASN_STRUCT_FREE_CONTENTS_ONLY() offer no guarantees that the structure is left in a valid initialized/zeroed state. In that case I think *_free() functions should never expend effort on things like zeroing pointers after free(), because that just masks that this cannot be relied on.
The text was updated successfully, but these errors were encountered: