Skip to content

systemd: fix CPU quota overflow and unified cpu.max round-up - #79

Open
kolyshkin wants to merge 2 commits into
opencontainers:mainfrom
kolyshkin:systemd-cpu-quota-overflow
Open

kolyshkin wants to merge 2 commits into
opencontainers:mainfrom
kolyshkin:systemd-cpu-quota-overflow

Conversation

@kolyshkin

@kolyshkin kolyshkin commented Oct 2, 2026 •

Copy link
Copy Markdown
Contributor

systemd: fix CPU quota overflow in addCPUQuota

addCPUQuota converts CFS quota to CPUQuotaPerSecUSec, and, if the value
is rounded up, converts it back to quota. Both conversions are done
using int64 multiplication, which overflows for a large quota. As a
result, a large quota is converted to a tiny (or negative) value, which
is either rejected by the kernel or means "unlimited" (e.g. quota of
9223372036854700 becomes 290, and 10000000000001 becomes -8446744072709).

Fix this by:

  • doing the calculations in uint64;
  • rearranging the write-back calculation to avoid an overflow;
  • treating a quota above maxCPUQuota (the kernel limit of 2^44-1 us,
    which is low enough for quota * 1000000 to fit into uint64), either
    requested or resulting from the round-up, as unlimited, for both
    systemd and cgroupfs. Such quota is rejected by the kernel anyway,
    and it is effectively unlimited.

Fixes #73.

systemd: write rounded cpu.max quota to cgroupfs

Commit 83f2519 made the systemd driver write the rounded CPU quota to
cgroupfs, to keep it consistent with what systemd writes. This was not
done for the quota set via Unified["cpu.max"], so the rounded value
is passed to systemd, while the original value is written to cgroupfs.

Similarly, a quota too large for the kernel is now passed to systemd
as unlimited, but is written to cgroupfs as is, resulting in EINVAL.

Fix this by updating res["cpu.max"] in unifiedResToSystemdProps if
addCPUQuota modified the quota. To avoid modifying the caller's map,
clone r.Unified in UnifiedManager.Set (where r is already copied for
the same reason).

@kolyshkin
kolyshkin requested a review from a team as a code owner October 2, 2026 06:26
@kolyshkin
kolyshkin force-pushed the systemd-cpu-quota-overflow branch 2 times, most recently from 6cbfe93 to fa7c9e6 Compare October 2, 2026 06:43
@kolyshkin kolyshkin changed the title systemd: fix CPU quota overflow in addCPUQuota systemd: fix CPU quota overflow and unified cpu.max round-up Oct 2, 2026
@kolyshkin kolyshkin added this to the 0.1.1 milestone Oct 2, 2026
@kolyshkin
kolyshkin force-pushed the systemd-cpu-quota-overflow branch from 1b9eeab to 8e69244 Compare October 2, 2026 17:35
addCPUQuota converts CFS quota to CPUQuotaPerSecUSec, and, if the value
is rounded up, converts it back to quota. Both conversions are done
using int64 multiplication, which overflows for a large quota. As a
result, a large quota is converted to a tiny (or negative) value, which
is either rejected by the kernel or means "unlimited" (e.g. quota of
9223372036854700 becomes 290, and 10000000000001 becomes -8446744072709).

Fix this by:
 - doing the calculations in uint64;
 - rearranging the write-back calculation to avoid an overflow;
 - treating a quota above maxCPUQuota (the kernel limit of 2^44-1 us,
   which is low enough for quota * 1000000 to fit into uint64), either
   requested or resulting from the round-up, as unlimited, for both
   systemd and cgroupfs. Such quota is rejected by the kernel anyway,
   and it is effectively unlimited.

Fixes opencontainers#73.

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
@kolyshkin
kolyshkin force-pushed the systemd-cpu-quota-overflow branch from 8e69244 to 7b24f04 Compare October 2, 2026 17:38
Comment thread systemd/systemd_test.go
name: "Quota too large",
quota: maxCPUQuota + 1,
expectedCPUQuotaPerSecUSec: math.MaxUint64,
expectedQuota: -1,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps silly but should we define a const for the magic -1 so that it's clear we're talking about "unlimited" (not an accidental negative)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd keep this as is -- in cgroupfs, -1 always means "unlimited" and the value is used in this repo quite a lot of times. If we want to change it, let's change it everywhere (and not in this PR).

Comment thread systemd/systemd_test.go Outdated
},
{
name: "cpu.max",
minVer: 242, // For CPUQuotaPeriodUSec.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The other PR added consts for this I think?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right! Thanks, fixed.

Commit 83f2519 made the systemd driver write the rounded CPU quota to
cgroupfs, to keep it consistent with what systemd writes. This was not
done for the quota set via Unified["cpu.max"], so the rounded value
is passed to systemd, while the original value is written to cgroupfs.

Similarly, a quota too large for the kernel is now passed to systemd
as unlimited, but is written to cgroupfs as is, resulting in EINVAL.

Fix this by updating res["cpu.max"] in unifiedResToSystemdProps if
addCPUQuota modified the quota. To avoid modifying the caller's map,
clone r.Unified in UnifiedManager.Set (where r is already copied for
the same reason).

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
@kolyshkin
kolyshkin force-pushed the systemd-cpu-quota-overflow branch from 7b24f04 to 0a10b5b Compare October 2, 2026 20:12
@kolyshkin
kolyshkin requested a review from thaJeztah October 2, 2026 20:13
@kolyshkin

Copy link
Copy Markdown
Contributor Author

@thaJeztah ptal

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

systemd: addCPUQuota overflows int64 for a large CPU quota, writing a tiny value

2 participants