<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Correctness on SILO</title>
    <link>https://silo.pgsty.com/tags/correctness/</link>
    <description>Recent content in Correctness on SILO</description>
    <generator>Hugo</generator>
    <language>en</language>
    
    
    
      <lastBuildDate>Sat, 24 Oct 2026 18:19:49 +0800</lastBuildDate>
    
    
      <atom:link href="https://silo.pgsty.com/tags/correctness/index.xml" rel="self" type="application/rss+xml" />
    
    <item>
        <title>Duplicate Part Numbers in CompleteMultipartUpload</title>
        <link>https://silo.pgsty.com/blog/security/duplicate-part-numbers/</link>
        <pubDate>Mon, 03 Aug 2026 00:00:00 +0000</pubDate>
        
        <guid>https://silo.pgsty.com/blog/security/duplicate-part-numbers/</guid>
        <description>&lt;p&gt;In &lt;code&gt;CompleteMultipartUpload&lt;/code&gt;, the S3 API requires part numbers in the completion XML to be strictly ascending. Up through Server &lt;code&gt;20260725&lt;/code&gt;, SILO enforced that invariant with &lt;code&gt;sort.SliceIsSorted&lt;/code&gt; and a strict &lt;code&gt;&amp;lt;&lt;/code&gt; comparator—which accepts non-decreasing sequences such as &lt;code&gt;[1, 1]&lt;/code&gt; and duplicates the referenced part in the assembled object. &lt;a href=&#34;https://github.com/pgsty/silo/releases/tag/RELEASE.2026-08-04T00-00-00Z&#34;&gt;Server 20260804&lt;/a&gt; (&lt;a href=&#34;https://github.com/pgsty/silo/issues/49&#34;&gt;&lt;code&gt;pgsty/silo#49&lt;/code&gt;&lt;/a&gt;) replaces that check with an explicit adjacent-pair comparison (&lt;code&gt;Parts[i-1].PartNumber &amp;gt;= Parts[i].PartNumber&lt;/code&gt;) and fixes an adjacent empty-part panic in &lt;code&gt;ReadParts&lt;/code&gt; (&lt;code&gt;2602177ef&lt;/code&gt;).&lt;/p&gt;&#xA;&lt;h2 id=&#34;summary&#34;&gt;Conclusions first&#xA;&lt;/h2&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&lt;strong&gt;The bug is real and small&lt;/strong&gt;: &lt;code&gt;CompleteMultipartUpload&lt;/code&gt; used &lt;code&gt;sort.SliceIsSorted&lt;/code&gt; with &lt;code&gt;&amp;lt;&lt;/code&gt;, which tests non-decreasing order (&lt;code&gt;a[0] &amp;lt;= a[1] &amp;lt;= ...&lt;/code&gt;) rather than strictly increasing order (&lt;code&gt;a[0] &amp;lt; a[1] &amp;lt; ...&lt;/code&gt;). Passing duplicate part numbers such as &lt;code&gt;[1, 1]&lt;/code&gt; succeeded and duplicated the underlying part in the final object.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;This is a data-integrity defect, not a CVE&lt;/strong&gt;: Triggering it requires a caller who already holds &lt;code&gt;s3:PutObject&lt;/code&gt; permission on the target key and a valid &lt;code&gt;uploadId&lt;/code&gt; for an in-progress multipart upload. It does not cross tenant, bucket, or credential boundaries.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;The fix stays at the HTTP handler boundary&lt;/strong&gt;: &lt;code&gt;cmd/object-handlers.go&lt;/code&gt; rejects &lt;code&gt;Parts[i-1].PartNumber &amp;gt;= Parts[i].PartNumber&lt;/code&gt; with &lt;code&gt;InvalidPartOrder&lt;/code&gt; (HTTP 400). Sparse part numbers (&lt;code&gt;[1, 3]&lt;/code&gt;, &lt;code&gt;[5, 9]&lt;/code&gt;, &lt;code&gt;[3]&lt;/code&gt;) remain valid.&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;hr&gt;&#xA;&lt;h2 id=&#34;the-defect&#34;&gt;Why &lt;code&gt;sort.SliceIsSorted&lt;/code&gt; Accepted Duplicates&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;In &lt;code&gt;cmd/object-handlers.go&lt;/code&gt;, &lt;code&gt;CompleteMultipartUploadHandler&lt;/code&gt; validated the parsed &lt;code&gt;&amp;lt;CompleteMultipartUpload&amp;gt;&lt;/code&gt; part list with:&lt;/p&gt;</description>
      </item>
    
  </channel>
</rss>
