Skip to content

CBOR @CborLabel with negative label value decodes incorrectly #3190

Description

@ardune

Describe the bug
When using negative values for @CborLabel when combined with preferCborLabelsOverNames decoding fails

To Reproduce
Quick Test file

package kotlinx.serialization.cbor

import kotlinx.serialization.*
import kotlin.test.*

class NegativeLabelsOverNamesTest {
    private val withLabel = Cbor {
        preferCborLabelsOverNames = true
        useDefiniteLengthEncoding = true
    }

    @Test
    fun withNegativeTwoLabel() {
        val target = WithNegativeTwo(
            3,
            true
        )
        // a2010321f5
        /**
         * A2    # map(2)
         *    01 # unsigned(1)
         *    03 # unsigned(3)
         *    21 # negative(1)
         *    F5 # primitive(21)
         */
        val bytes = withLabel.encodeToByteArray(target)

        // failure - kotlinx.serialization.cbor.internal.CborDecodingException: Expected an unsigned or negative integer, but found F5
        val decodeBack: WithNegativeTwo = withLabel.decodeFromByteArray(bytes)
        assertEquals(target, decodeBack)
    }

    @Test
    fun withNegativeOneLabel() {
        val target = WithNegativeOne(
            3,
            true
        )
        //a2010320f5
        /**
         * A2    # map(2)
         *    01 # unsigned(1)
         *    03 # unsigned(3)
         *    20 # negative(0)
         *    F5 # primitive(21)
         */
        val bytes = withLabel.encodeToByteArray(target)

        // failure - kotlinx.serialization.cbor.internal.CborDecodingException: CborLabel unknown: 0 for kotlinx.serialization.cbor.NegativeLabelsOverNamesTest.WithNegativeOne(id: kotlin.Int, cool: kotlin.Boolean?)
        val decodeBack: WithNegativeOne = withLabel.decodeFromByteArray(bytes)
        assertEquals(target, decodeBack)
    }

    @OptIn(ExperimentalSerializationApi::class)
    @Serializable
    data class WithNegativeTwo(
        @CborLabel(1)
        val id: Int,
        // critical bit: negative label causes failure to decode
        @CborLabel(-2)
        val cool: Boolean? = null,
    )

    @OptIn(ExperimentalSerializationApi::class)
    @Serializable
    data class WithNegativeOne(
        @CborLabel(1)
        val id: Int,
        // critical bit: negative label causes failure to decode
        @CborLabel(-1)
        val cool: Boolean? = null,
    )
}

Expected behavior
Tests should pass.

Fix
The fix I applied locally to 1.11.0 is attached as a patch (one liner):

kotlinx.serialization-3-41-26 PM.patch

This does change that line from:
e334d1c
Which was intended to make reading strictly follow the RFC - but since it only allowed positive numbers I just changed it to the generic 'readNumber' to handle both positive and negative.

Environment

  • Kotlin version: 2.3.20
  • Library version: 1.11.0
  • Kotlin platforms: All
  • Gradle version: 8.7

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions