<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://manicpop.org/gewiki/index.php?action=history&amp;feed=atom&amp;title=Classic_bug%3A_Cargo_weight_overflow</id>
	<title>Classic bug: Cargo weight overflow - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://manicpop.org/gewiki/index.php?action=history&amp;feed=atom&amp;title=Classic_bug%3A_Cargo_weight_overflow"/>
	<link rel="alternate" type="text/html" href="https://manicpop.org/gewiki/index.php?title=Classic_bug:_Cargo_weight_overflow&amp;action=history"/>
	<updated>2026-10-20T16:30:15Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.46.2</generator>
	<entry>
		<id>https://manicpop.org/gewiki/index.php?title=Classic_bug:_Cargo_weight_overflow&amp;diff=2173&amp;oldid=prev</id>
		<title>CodexBot: Link bug discovery follow-up</title>
		<link rel="alternate" type="text/html" href="https://manicpop.org/gewiki/index.php?title=Classic_bug:_Cargo_weight_overflow&amp;diff=2173&amp;oldid=prev"/>
		<updated>2026-10-18T18:23:57Z</updated>

		<summary type="html">&lt;p&gt;Link bug discovery follow-up&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;{{Classic bug&lt;br /&gt;
|id=GE-BUG-001&lt;br /&gt;
|affected=Classic 3.2e and published classic source&lt;br /&gt;
|status=Verified in source&lt;br /&gt;
|fixed=Initially ge-next 0.1; comprehensively rewritten in ge-next 0.3.1&lt;br /&gt;
|source=GEFUNCS.C: chkweight() and calcweight()&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Summary ==&lt;br /&gt;
Classic calculates an item&amp;#039;s cargo contribution by multiplying its count by its configured weight before dividing by 100. The intermediate multiplication uses 32-bit arithmetic and can wrap even when the later division would produce a representable result.&lt;br /&gt;
&lt;br /&gt;
Because the same calculation is used for inventory reports and capacity checks, this is not merely a display error. A wrapped result can allow a ship to load vastly more cargo than its class permits.&lt;br /&gt;
&lt;br /&gt;
== Symptoms ==&lt;br /&gt;
A heavily loaded ship can appear to have little or no cargo weight, accept transfers far beyond its capacity, or reject a transfer whose wrapped arithmetic happens to produce a large result. Once an overflowing inventory exists, later reports and capacity decisions can change abruptly as item counts alter the wrapped remainder.&lt;br /&gt;
&lt;br /&gt;
Corrupted records and sysop testing can reach the condition immediately, but stock classic planet populations are large enough for a player to exploit some item types during ordinary play.&lt;br /&gt;
&lt;br /&gt;
== Exploit example ==&lt;br /&gt;
In the stock configuration, 100 men weigh 100 tons, so each man weighs one ton. Suppose an owned planet has at least 42,949,673 men and an empty ship requests:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
TRANSFER UP 42949673 MEN&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The true cargo contribution is 42,949,673 tons. Classic evaluates the numerator in 32-bit unsigned arithmetic:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
42,949,673 × 100 = 4,294,967,300&lt;br /&gt;
                     = 2^32 + 4&lt;br /&gt;
wrapped numerator    = 4&lt;br /&gt;
wrapped cargo weight = 4 / 100 = 0 tons&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The separate item-count overflow guard still passes because 42,949,673 is well below its limit. The capacity check therefore treats the transfer as adding zero tons and accepts tens of millions of men even on a small ship. [[Report command|&amp;lt;code&amp;gt;REPORT INVENTORY&amp;lt;/code&amp;gt;]] repeats the same multiplication and also omits their real weight.&lt;br /&gt;
&lt;br /&gt;
Troops make the exploit directly useful in planetary combat. Stock troops weigh 200 tons per 100. A transfer of 21,474,837 troops really weighs 42,949,674 tons, but:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
21,474,837 × 200 = 4,294,967,400&lt;br /&gt;
                     = 2^32 + 104&lt;br /&gt;
wrapped cargo weight = 104 / 100 = 1 ton&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
A player who accumulates that population on an owned planet can therefore move an enormous invasion force in a ship that classic believes gained only one ton. Similar wrap points exist for every item weight.&lt;br /&gt;
&lt;br /&gt;
These examples follow deterministically from the published source and stock weights. A live classic test would confirm the complete command path and messages, but is not required to establish the arithmetic failure.&lt;br /&gt;
&lt;br /&gt;
== Cause ==&lt;br /&gt;
Classic performs the equivalent of:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
(item_count × item_weight) / 100&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;item_count&amp;lt;/code&amp;gt; is an unsigned long and &amp;lt;code&amp;gt;item_weight&amp;lt;/code&amp;gt; is a long. Under the target compiler&amp;#039;s usual arithmetic conversions, the multiplication is performed as a 32-bit unsigned value. It wraps modulo 2^32 before division can reduce it. Summing multiple already-wrapped item contributions cannot recover the lost high bits.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;chkweight()&amp;lt;/code&amp;gt; performs this calculation for every item already aboard and again for the proposed addition. &amp;lt;code&amp;gt;calcweight()&amp;lt;/code&amp;gt; performs the same calculation for the inventory report. Their agreement therefore makes the incorrect result look internally consistent.&lt;br /&gt;
&lt;br /&gt;
== Fix history ==&lt;br /&gt;
=== ge-next 0.1 ===&lt;br /&gt;
ge-next 0.1 initially addressed the classic defect by calculating cargo contributions with floating-point arithmetic and by checking item-count addition against the unsigned-long limit before accepting a transfer. ge-next 0.1.1 refined the displayed total by accumulating fractional weight in a &amp;lt;code&amp;gt;double&amp;lt;/code&amp;gt; and rounding the final result upward.&lt;br /&gt;
&lt;br /&gt;
This removed the classic 32-bit multiplication wrap for normal game values, but later exact-integer cargo work and extreme inventory testing exposed remaining edge cases involving very large totals, fractional truncation, and intermediate overflow. The cargo system was therefore revisited rather than treating the original 0.1 change as the final implementation.&lt;br /&gt;
&lt;br /&gt;
=== ge-next 0.3.1 ===&lt;br /&gt;
ge-next 0.3.1 rewrote the cargo routines with functionally equivalent but architecture-specific implementations.&lt;br /&gt;
&lt;br /&gt;
The 16-bit build cannot rely on an efficient native 64-bit integer. It splits each quantity into complete groups of 100 and a remainder below 100, checks each multiplication against the remaining capacity before performing it, and carries fractional hundredths of a ton between item types. Impossible totals saturate instead of wrapping. Its “room for item” routine uses the same capacity predicate through a binary search, ensuring that displayed weight, transfer checks, and maximum transferable amounts follow one fit rule.&lt;br /&gt;
&lt;br /&gt;
The 32-bit build uses a 64-bit unsigned accumulator measured in hundredths of a ton. It can multiply a 32-bit item count by the configured item weight directly, calculate remaining capacity in one operation, and then apply the same rounding and saturation rules as the 16-bit build.&lt;br /&gt;
&lt;br /&gt;
Both implementations reject item-count addition overflow, combine fractional weight across item types, round any final fractional ton upward for display, and return a saturated maximum for impossible or corrupted inventories.&lt;br /&gt;
&lt;br /&gt;
== Discovery transcript ==&lt;br /&gt;
[[Discovery transcript: GE-BUG-001|Read the June 2026 discussion of a later ge-next cargo-calculation failure]].&lt;br /&gt;
&lt;br /&gt;
== Compatibility notes ==&lt;br /&gt;
A port should not assume that using a wider accumulator alone resolves every case. It must also reject item-count overflow, define behavior for totals beyond the report type, combine fractional weights consistently, and ensure that reports, capacity checks, loot collection, and “maximum transfer” calculations all use the same rule.&lt;br /&gt;
&lt;br /&gt;
== Verification ==&lt;br /&gt;
The classic defect and exploit arithmetic were verified against the published classic source and stock item weights. The ge-next 0.1, 0.1.1, and 0.3.1 implementations were checked against their corresponding source revisions. The 0.3.1 routines were also exercised with inventories near the 32-bit unsigned item-count limit.&lt;br /&gt;
&lt;br /&gt;
[[Category:Classic bugs: Numeric correctness]]&lt;br /&gt;
&lt;br /&gt;
{{GE navigation}}&lt;/div&gt;</summary>
		<author><name>CodexBot</name></author>
	</entry>
</feed>