[ 
https://issues.apache.org/jira/browse/THRIFT-6124?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Dmytro Shteflyuk updated THRIFT-6124:
-------------------------------------
    Description: 
h3. Problem

The Ruby {{Deserializer}} API accepts an existing generated object as its 
target. Struct and union readers currently assign only fields that are present 
in the incoming payload; they do not clear values left by an earlier 
deserialization.

For structs, an optional field omitted from the next payload therefore keeps 
its previous value. A field whose incoming wire type does not match the 
generated definition can have the same result. Generated defaults also retain 
caller-modified values instead of being restored before the next message is 
read.

For unions, an unknown or mismatched field is skipped. If the target already 
contains a variant, the old discriminator and value remain in place, so 
validation can succeed even though the new payload did not contain that variant.

h3. Client impact

Applications that reuse generated Ruby objects to reduce allocations can 
observe values that were not present in the latest message. Required-field 
validation can also be satisfied by a value retained from an earlier message. A 
reused union can represent an unknown newer field as an unrelated variant from 
the preceding message.

The behavior is present in both the pure-Ruby and native readers.

h3. Reproduction

Given these generated types:

{code}
struct Record {
  1: optional string note
}

union Choice {
  1: string text
}
{code}

Reusing one {{Record}} instance retains the first message's optional value:

{code:ruby}
serializer = Thrift::Serializer.new
deserializer = Thrift::Deserializer.new
target = Record.new

deserializer.deserialize(
  target,
  serializer.serialize(Record.new(note: "old"))
)
puts target.note
# old

deserializer.deserialize(
  target,
  serializer.serialize(Record.new)
)
puts target.note.inspect
# "old", although the second payload omitted the field
{code}

The equivalent union case occurs when a reused {{Choice}} containing {{text}} 
reads a payload containing only an unknown field: the previous {{text}} 
discriminator and value remain set.

h3. Expected behavior

Every read should begin from the same field state as a newly initialized 
target: declared defaults restored, other struct fields unset, and the union 
discriminator and value cleared.

Absent, unknown, or type-mismatched fields must not expose values from an 
earlier message. Required-field validation must evaluate only the current 
message. If reading fails partway through, values that existed before that read 
must not survive; the target should not be treated as a successfully 
deserialized value.


  was:
h3. Problem

The Ruby {{Deserializer}} API accepts an existing generated object as its 
target. Struct and union readers currently assign only fields that are present 
in the incoming payload; they do not clear values left by an earlier 
deserialization.

For structs, an optional field omitted from the next payload therefore keeps 
its previous value. A field whose incoming wire type does not match the 
generated definition can have the same result. Generated defaults also retain 
caller-modified values instead of being restored before the next message is 
read.

For unions, an unknown or mismatched field is skipped. If the target already 
contains a variant, the old discriminator and value remain in place, so 
validation can succeed even though the new payload did not contain that variant.

h3. Client impact

Applications that reuse generated Ruby objects to reduce allocations can 
observe values that were not present in the latest message. Required-field 
validation can also be satisfied by a value retained from an earlier message. A 
reused union can represent an unknown newer field as an unrelated variant from 
the preceding message.

The behavior is present in both the pure-Ruby and native readers.

h3. Reproduction

Given these generated types:

{code:thrift}
struct Record {
  1: optional string note
}

union Choice {
  1: string text
}
{code}

Reusing one {{Record}} instance retains the first message's optional value:

{code:ruby}
serializer = Thrift::Serializer.new
deserializer = Thrift::Deserializer.new
target = Record.new

deserializer.deserialize(
  target,
  serializer.serialize(Record.new(note: "old"))
)
puts target.note
# old

deserializer.deserialize(
  target,
  serializer.serialize(Record.new)
)
puts target.note.inspect
# "old", although the second payload omitted the field
{code}

The equivalent union case occurs when a reused {{Choice}} containing {{text}} 
reads a payload containing only an unknown field: the previous {{text}} 
discriminator and value remain set.

h3. Expected behavior

Every read should begin from the same field state as a newly initialized 
target: declared defaults restored, other struct fields unset, and the union 
discriminator and value cleared.

Absent, unknown, or type-mismatched fields must not expose values from an 
earlier message. Required-field validation must evaluate only the current 
message. If reading fails partway through, values that existed before that read 
must not survive; the target should not be treated as a successfully 
deserialized value.



> Ruby deserializer retains stale values when reusing target objects
> ------------------------------------------------------------------
>
>                 Key: THRIFT-6124
>                 URL: https://issues.apache.org/jira/browse/THRIFT-6124
>             Project: Thrift
>          Issue Type: Bug
>          Components: Ruby - Library
>            Reporter: Dmytro Shteflyuk
>            Assignee: Dmytro Shteflyuk
>            Priority: Major
>
> h3. Problem
> The Ruby {{Deserializer}} API accepts an existing generated object as its 
> target. Struct and union readers currently assign only fields that are 
> present in the incoming payload; they do not clear values left by an earlier 
> deserialization.
> For structs, an optional field omitted from the next payload therefore keeps 
> its previous value. A field whose incoming wire type does not match the 
> generated definition can have the same result. Generated defaults also retain 
> caller-modified values instead of being restored before the next message is 
> read.
> For unions, an unknown or mismatched field is skipped. If the target already 
> contains a variant, the old discriminator and value remain in place, so 
> validation can succeed even though the new payload did not contain that 
> variant.
> h3. Client impact
> Applications that reuse generated Ruby objects to reduce allocations can 
> observe values that were not present in the latest message. Required-field 
> validation can also be satisfied by a value retained from an earlier message. 
> A reused union can represent an unknown newer field as an unrelated variant 
> from the preceding message.
> The behavior is present in both the pure-Ruby and native readers.
> h3. Reproduction
> Given these generated types:
> {code}
> struct Record {
>   1: optional string note
> }
> union Choice {
>   1: string text
> }
> {code}
> Reusing one {{Record}} instance retains the first message's optional value:
> {code:ruby}
> serializer = Thrift::Serializer.new
> deserializer = Thrift::Deserializer.new
> target = Record.new
> deserializer.deserialize(
>   target,
>   serializer.serialize(Record.new(note: "old"))
> )
> puts target.note
> # old
> deserializer.deserialize(
>   target,
>   serializer.serialize(Record.new)
> )
> puts target.note.inspect
> # "old", although the second payload omitted the field
> {code}
> The equivalent union case occurs when a reused {{Choice}} containing {{text}} 
> reads a payload containing only an unknown field: the previous {{text}} 
> discriminator and value remain set.
> h3. Expected behavior
> Every read should begin from the same field state as a newly initialized 
> target: declared defaults restored, other struct fields unset, and the union 
> discriminator and value cleared.
> Absent, unknown, or type-mismatched fields must not expose values from an 
> earlier message. Required-field validation must evaluate only the current 
> message. If reading fails partway through, values that existed before that 
> read must not survive; the target should not be treated as a successfully 
> deserialized value.



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to