> On Nov 11, 2026, at 17:04, Evgeniy Gorbanyov <[email protected]> wrote:
>
> Hi,
>
> load_relcache_init_file() treats a zero-length fread() of the next
> relation descriptor length as the end of pg_internal.init, which has
> no record count. fread() also returns zero when a read error
> transfers no data. In that case the file is accepted whenever the
> nailed rel and index counts already match, so a partial cache can be
> installed instead of bootstrapping it from the built-in descriptors.
>
> Check ferror() before treating a zero-length read as the end of the
> file.
>
> The patch is attached.
>
> Regards,
> Evgeniy Gorbanyov
> <0001-Distinguish-EOF-from-a-read-error-when-loading-the-r.patch>
```
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -6329,7 +6329,7 @@ load_relcache_init_file(bool shared)
nread = fread(&len, 1, sizeof(len), fp);
if (nread != sizeof(len))
{
- if (nread == 0)
+ if (nread == 0 && !ferror(fp))
break; /* end of file */
goto read_failed;
}
```
From the comment "/* end of file */" and the subsequent "goto read_failed"
statement, it looks like this code intentionally distinguishes a normal EOF
from a read failure. It also treats a partial read as a failure, which could
indicate file corruption or truncation.
When no bytes are read, checking ferror(fp) or feof(fp) is necessary to
distinguish a normal EOF from a read error. Otherwise, a read error could be
silently treated as EOF.
Specific to this call site, without the patch, if an IO error happens to occur
while reading no bytes, load_relcache_init_file() could incorrectly return
true, even though the initialization file was not fully read.
I also searched the source tree for other fread() call sites, and this seems to
be the only one missing an ferror() check.
So, this patch looks good to me. Since this code has been around for a long
time, I think it's worth back-patching to all supported branches.
Best regards,
--
Chao Li (Evan)
HighGo Software Co., Ltd.
https://www.highgo.com/