pjfanning opened a new issue, #3749:
URL: https://github.com/apache/pekko/issues/3749

   ### Summary
   
   Since #3495, `Serialization.deserialize(bytes, 0, manifest)` with a 
non-empty manifest returns `Success(null)`, whatever the bytes contain. Before 
#3495 it failed with `NotSerializableException` unless the manifest happened to 
name a loadable class.
   
   A corrupt persisted record that resolves to serializer id 0 can therefore 
load as a `null` payload instead of failing. Callers that rely on a failure to 
fall back to something else never get one.
   
   **#3495 should not be reverted.** It removes a class-loading oracle and 
unbounded metaspace growth driven by wire-supplied manifests. This issue is 
about the narrower `NullSerializer` consequence.
   
   ### Details
   
   `NullSerializer` has id 0, declares `includeManifest = false`, and is always 
registered. Its `fromBinary` ignores its input and returns `null`.
   
   For a manifest that isn't a loadable class:
   
   | Pekko version | Call | Result |
   |---|---|---|
   | 2.0.0-M4 | `deserialize(garbage, 0, "not-a-class")` | 
`Failure(NotSerializableException("Cannot find manifest class ..."))` |
   | 2.0.0 | `deserialize(garbage, 0, "not-a-class")` | `Success(null)` |
   
   The same change applies to other serializers with `includeManifest = false`, 
such as `ByteArraySerializer` and custom ones. They now ignore an unresolvable 
manifest, which is the stated intent of #3495. `NullSerializer` stands out 
because the result is `null` and the input is discarded.
   
   The empty-manifest path is not new: `deserialize(garbage, 0, "")` already 
returned `Success(null)` before #3495. #3495 widened that existing behaviour to 
non-empty manifests.
   
   Id 0 is easy to reach by accident. It's the default for a missing int: for 
example, the Cassandra driver's `Row.getInt` returns 0 for a null or empty 
column.
   
   ### How this was found
   
   Updating pekko-persistence-cassandra to Pekko 2.0.0 made two 
`CassandraSnapshotStoreSpec` tests fail: apache/pekko-persistence-cassandra#521.
   
   The root cause there is a bug in the spec, which binds `ser_id` and 
`ser_manifest` in the wrong order. The stored row therefore had an empty 
`ser_id` and a garbage manifest, and the tests only passed because of the old 
manifest lookup failure. On 2.0.0 the snapshot store received 
`SelectedSnapshot(metadata, null)` rather than a failure, so it did not fall 
back to an older snapshot.
   
   The spec is being fixed in that PR, so this does not block 
pekko-persistence-cassandra. The plugin always writes a real `ser_id` itself, 
so I'm not aware of this affecting production data. Other plugins or user code 
reading stored serializer ids could be exposed in the same way.
   
   ### Reproducer
   
   Derived from reading the source and the CI failure above. I have not run 
this snippet in isolation.
   
   ```scala
   val serialization = SerializationExtension(system)
   serialization.deserialize("garbage".getBytes(StandardCharsets.UTF_8), 0, 
"not-a-class")
   // 2.0.0-M4: Failure(NotSerializableException)
   // 2.0.0:    Success(null)
   ```
   
   ### Possible ways forward
   
   These all keep #3495 in place:
   
   1. **Make `NullSerializer.fromBinary` reject non-empty input (suggested).** 
`toBinary` only ever produces an empty array, so non-empty bytes can't have 
come from a conforming sender. Throwing `NotSerializableException` restores a 
failure for this case without any class loading, and also closes the existing 
empty-manifest path. The behaviour change for anything relying on the current 
leniency would need a note.
   2. **Document it only.** Add a release/migration note that deserializing 
with an `includeManifest = false` serializer no longer fails on an unresolvable 
manifest, and that id 0 yields `null`.
   
   ### References
   
   - #3495 (`cffd027b89`), first released in 2.0.0-RC1 and 2.0.0
   - #3478
   - apache/pekko-persistence-cassandra#521
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to