---
title: "fail-closed 값을 Long.MAX_VALUE 로 두면 합산에서 넘쳐 통과한다"
description: "확인할 수 없는 크기를 Long.MAX_VALUE 로 표현했더니 합산에서 -2 로 넘쳐 한도 검사를 통과했습니다. 유한 상한과 포화 덧셈으로 고친 과정을 적었습니다."
date: 2026-10-21
updated: 2026-10-21T09:35:29.309Z
tags: [java, overflow, validation, fail-closed]
canonical: https://blog.wooncloud.com/posts/long-max-value-overflow-fail-closed
---

업로드 용량 검사를 손보다가, 서버가 크기를 확인할 수 없는 첨부를 어떻게 다룰지 정해야 했습니다. 확인이 안 되면 통과시키는 게 아니라 막아야 합니다(fail-closed).

그런데 검사 코드를 감싼 쪽이 예외를 삼키고 통과시키는 구조였습니다. 그래서 예외를 던지는 대신 **크기 값 자체를 무한대로 돌려주기로** 했습니다.

## 정수에는 무한대가 없습니다

자바에는 정수 무한대가 없으니 `Long.MAX_VALUE` 를 쓰고 싶어집니다. 어떤 한도보다 크니 항상 막히겠죠?

```java
long total = 0;
for (Item item : items) {
    total += sizeOf(item);   // 확인 불가 항목은 Long.MAX_VALUE
}
if (total > limit) reject();
```

확인 불가 항목이 하나일 때는 맞습니다. 그런데 둘이 되면 `Long.MAX_VALUE + Long.MAX_VALUE` 가 예외 없이 감겨 돌아서 `-2` 가 됩니다.

![Long.MAX_VALUE를 그대로 더하면 넘쳐 음수가 되지만 포화 덧셈은 상한에서 멈춘다.](/images/posts/long-max-value-overflow-fail-closed/1aa1c15b-314e-48f3-8ab8-5247a56961b5.webp)

![Long.MAX_VALUE 두 개를 그대로 더하면 합이 -2 로 뒤집혀 한도 검사를 통과하지만, 2^50 상한에서 멈추는 포화 덧셈을 쓰면 합이 상한에 머물러 거부된다.](/images/posts/long-max-value-overflow-fail-closed/b6fdcd01-6aaa-48fc-8bf6-9e4e5d357cdd.svg)

막으려고 넣은 값이 우회 수단이 된 셈입니다. 요청에 확인 불가 항목을 두 개 이상 섞기만 하면 됩니다.

합을 다른 서비스에 넘겨 거기서 `usage + incoming` 을 계산하게 해도 똑같이 넘칩니다. 그쪽이 JavaScript 라면 2^53 을 넘는 순간 정밀도부터 깨집니다.

## 유한한 상한과 포화 덧셈

고치는 방법은 두 가지를 같이 쓰는 겁니다.

먼저 무한대 대신 **유한한 상한**을 씁니다. 어떤 정상 한도보다 확실히 크면서, 여러 번 더해도 넘치지 않는 값이면 됩니다. 1 PiB(2^50)면 충분하고, JS 안전 정수(2^53) 안이라 다른 쪽으로 넘겨도 정밀도가 유지됩니다.

그리고 합은 그 상한에서 멈추게 합니다. 이른바 포화 덧셈입니다.

```java
static final long UNVERIFIED = 1L << 50;   // 1 PiB. 어떤 한도보다 크고, 더해도 넘치지 않는다

static long plus(long total, long bytes) {
    return Math.min(UNVERIFIED, total + bytes);
}

long total = 0;
for (Item item : items) {
    total = plus(total, sizeOf(item));      // 확인 불가 항목은 UNVERIFIED 를 돌려준다
}
if (total > limit) reject();
```

`total` 과 `bytes` 가 둘 다 상한 이하이면 `total + bytes` 는 2^51 을 넘지 않아서 중간 합도 `long` 범위 안입니다. 확인 불가 항목이 몇 개든 합은 2^50 에서 멈추고, 한도 비교는 항상 참이 됩니다. 정상 크기도 이 상한 아래라고 보는 전제입니다.

`Math.addExact` 는 넘치면 예외를 던지지만, 예외를 삼키는 구조에서는 쓸 수 없습니다.

## 기존 경로는 따로 봅니다

기존 동작을 바꾸면 안 되는 경로(기능 스위치가 꺼진 상태 등)에는 포화를 적용하지 않고, 새 판정 모드에서만 쓸 수도 있습니다. 다만 그 경로에 원래 있던 넘침 우회는 별도로 고쳐야 합니다.

"확인할 수 없음"을 값으로 표현할 때는 그 값이 지나갈 모든 산술(합, 차, 비교, 직렬화)을 한 번씩 따라가 봐야 합니다. 무한대 흉내는 산술 한 번에 부호가 뒤집힙니다.

## 참고

- [JLS 15.18.2 정수 덧셈 (넘침은 예외 없이 감겨 돈다)](https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.18.2)
- [Math.addExact(long, long)](https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#addExact-long-long-)
- [Number.MAX_SAFE_INTEGER (MDN)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MAX_SAFE_INTEGER)
