Skip to content

Reader.longCompare returns the wrong sign due to Long subtraction overflow #829

@haskiindahouse

Description

@haskiindahouse

borer-core Reader.longCompare computes the comparison via math.signum(long - value).toInt. When long and value straddle the Long range (e.g. Long.MaxValue vs Long.MinValue), the subtraction overflows and the sign is inverted.

Reproducer (scala-cli):

//> using scala 3.8.3
//> using dep io.bullet::borer-core:1.16.1

import io.bullet.borer.*

@main def repro070(): Unit =
  val encoded = Cbor.encode(Long.MaxValue).toByteArray
  val reader = Cbor.reader(encoded)
  reader.dataItem()

  val cmp = reader.longCompare(Long.MinValue)
  println(s"longCompare(Long.MinValue) when holding Long.MaxValue = $cmp")

Output (borer 1.16.1, Scala 3.8.3):

longCompare(Long.MinValue) when holding Long.MaxValue = -1

i.e. the reader claims Long.MaxValue < Long.MinValue. Reversing the test (reader holds Long.MinValue, compare against Long.MaxValue) returns +1, claiming the opposite.

Source

def longCompare(value: Long): Int =
if (hasLong)
val long = if (hasInt) receptacle.intValue.toLong else receptacle.longValue
math.signum(long - value).toInt
else Int.MaxValue

def longCompare(value: Long): Int =
  if (hasLong)
    val long = if (hasInt) receptacle.intValue.toLong else receptacle.longValue
    math.signum(long - value).toInt
  else Int.MaxValue

Long.MaxValue - Long.MinValue overflows to -1, and math.signum(-1) = -1. The same shape would affect tryReadLongCompare.

Suggested fix

Use java.lang.Long.compare(long, value) — it doesn't compute the difference and handles the full Long range:

def longCompare(value: Long): Int =
  if (hasLong)
    val long = if (hasInt) receptacle.intValue.toLong else receptacle.longValue
    java.lang.Long.compare(long, value)
  else Int.MaxValue

The existing tests don't cover extreme Long comparisons, which is presumably how this slipped through. Happy to PR with a regression test.

Versions

borer-core 1.16.1 / 1.16.2, Scala 3.8.3.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions