Repository navigation
[BUG] 谷歌网盘经 WebDAV 上传时进程崩溃:blockRefReadSeeker.Read 空指针,疑似请求体生命周期竞争 #3334
Copy link
Copy link
Open
Labels
Description
请确认以下事项
-
我已确认阅读并同意 AGPL-3.0 第15条 。
本程序不提供任何明示或暗示的担保,使用风险由您自行承担。 -
我已确认阅读并同意 AGPL-3.0 第16条 。
无论何种情况,版权持有人或其他分发者均不对使用本程序所造成的任何损失承担责任。 -
我确认我的描述清晰,语法礼貌,能帮助开发者快速定位问题,并符合社区规则。
-
我已确认阅读了OpenList文档。
-
我已确认没有重复的问题或讨论。
-
我已确认是
OpenList的问题,而不是其他原因(例如 网络 ,依赖或操作)。 -
我认为此问题必须由
OpenList处理,而非第三方。 -
我已确认这个问题在最新版本中没有被修复。
-
我没有阅读这个清单,只是闭眼选中了所有的复选框,请关闭这个 Issue 。
OpenList 版本(必填)
v4.2.9
使用的存储驱动(必填)
GoogleDrive
问题描述(必填)
现象
通过 WebDAV PUT(http://127.0.0.1:5244/dav/...,没有经过反向代理)向谷歌网盘上传一个约 88 GB 的文件。约 17 分钟后,OpenList 进程因空指针 panic 退出,容器被重启,上传中断。不是 OOM。
原因分析(根据源码推断)
以下引用的都是提交 0497ee8 的代码。
chunkUpload(drivers/google_drive/util.go#L335-L396)对每个分片的处理是:- 用
ss.GetSectionReader取得 reader(L349),用NewLimitedUploadStream包一层(L353); - 在
retry.Do里发起 PUT(L354–L365); retry.Do结束后调用ss.FreeSectionReader(reader)(L389)。
- 用
hybridSectionReader.FreeSectionReader(internal/stream/util.go#L292-L297)会把缓冲块放回复用缓存,并把内嵌的sr.ReadSeeker置为 nil。http.Client.Do返回后,transport 仍可能在writeLoop里继续读取请求体,请求体稍后才会被异步关闭。这段代码没有等请求体关闭,就释放了 reader。writeLoop再读时,就会在 nil 的io.ReadSeeker上调用Read。这正好对应堆栈里的<autogenerated>帧(addr=0x18)和limit.go:47。- panic 发生在
writeLoop这个 goroutine 里,上传流程里的 recover 接不住,所以整个进程退出。
同一个函数里的其他问题:
- 没有检查 HTTP 状态码。 是否成功只看响应 JSON 里有没有
error.code。收到 308 时,也没有根据Range头确认谷歌实际收到的范围。 Content-Length只写在了请求头里(L362),没有设置req.ContentLength。net/http发送客户端请求时会忽略这个请求头。
相关 issue
- [BUG] 上传文件到天翼云盘时OpenList崩溃 #1201:189CloudPC 上传时崩溃,堆栈相似(
writeLoop → RateLimitReader.Read空指针),已关闭,没有定位到根因。
修复建议
- 每次尝试前,确认上一轮请求体的读取已经结束,再
Seek回开头重试、复用缓冲块,或者调用FreeSectionReader。 - 设置
req.ContentLength = chunkSize。 - 按谷歌的续传协议处理状态码:
- 308:根据
Range头推进偏移量; - 200/201:上传完成;
- 5xx 和网络错误:先查询续传位置,再重试;
- 上传会话失效:重建会话。
- 308:根据
- 其他在请求结束后马上回收 section reader 的驱动,建议一并检查。
日志(必填)
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x18 pc=0xbf9e2c]
goroutine 857177 [running]:
github.com/OpenListTeam/OpenList/v4/internal/stream.(*blockRefReadSeeker).Read(0x1?, {0x9dfdf0dc000?, 0x8000?, 0x90f0a80?})
<autogenerated>:1 +0xc
github.com/OpenListTeam/OpenList/v4/internal/stream.(*RateLimitReader).Read(0x9dfdec4b740, {0x9dfdf0dc000, 0x8000, 0x8000})
/home/runner/work/OpenList/OpenList/internal/stream/limit.go:47 +0x5a
io.copyBuffer({0x7fe36de31620, 0x9dfdeb9e680}, {0x9491f48, 0x9dfdec4b740}, {0x9dfdf0dc000, 0x8000, 0x8000})
/opt/hostedtoolcache/go/1.27.3/x64/src/io/io.go:429 +0x190
io.CopyBuffer({0x7fe36de31620?, 0x9dfdeb9e680?}, {0x9491f48?, 0x9dfdec4b740?}, {0x9dfdf0dc000?, 0x9dfded7bc80?, 0x42657b?})
/opt/hostedtoolcache/go/1.27.3/x64/src/io/io.go:402 +0x36
net/http.(*transferWriter).doBodyCopy(0x9dfdea8ec80, {0x7fe36de31620, 0x9dfdeb9e680}, {0x9491f48, 0x9dfdec4b740})
/opt/hostedtoolcache/go/1.27.3/x64/src/net/http/transfer.go:425 +0xdc
net/http.(*transferWriter).writeBody(0x9dfdea8ec80, {0x9490308, 0x9dfdf5cc080})
/opt/hostedtoolcache/go/1.27.3/x64/src/net/http/transfer.go:369 +0x705
net/http.(*Request).write(0x9dfde88d540, {0x9490308, 0x9dfdf5cc080}, 0x0, 0x9dfdf5c34a0, 0x0)
/opt/hostedtoolcache/go/1.27.3/x64/src/net/http/request.go:772 +0xac6
net/http.(*persistConn).writeLoop(0x9dfde88d680)
/opt/hostedtoolcache/go/1.27.3/x64/src/net/http/transport.go:2836 +0x174
created by net/http.(*Transport).dialConn in goroutine 857170
/opt/hostedtoolcache/go/1.27.3/x64/src/net/http/transport.go:2124 +0x1e05
INFO[2026-10-31 12:23:15] reading config file: /opt/openlist/data/config.json
INFO[2026-10-31 12:23:15] load config from env with prefix:
INFO[2026-10-31 12:23:15] total memory: 23718MB, available: 12967MB
INFO[2026-10-31 12:23:15] min free memory: 1024MB
INFO[2026-10-31 12:23:15] max block limit: 64MB
INFO[2026-10-31 12:23:15] auto memory limit: 4MB
INFO[2026-10-31 12:23:15] init logrus...
start HTTP server @ 0.0.0.0:5244
配置文件内容(必填)
config.json(敏感字段已隐藏):
{
"force": false,
"site_url": "/",
"cdn": "",
"jwt_secret": "******",
"token_expires_in": "******",
"database": {
"type": "sqlite3",
"host": "",
"port": 0,
"user": "",
"password": "",
"name": "",
"db_file": "/opt/openlist/data/data.db",
"table_prefix": "x_",
"ssl_mode": "",
"dsn": ""
},
"meilisearch": {
"host": "http://localhost:7700",
"api_key": "",
"index": "openlist"
},
"scheme": {
"address": "0.0.0.0",
"http_port": 5244,
"https_port": -1,
"force_https": false,
"cert_file": "",
"key_file": "",
"unix_file": "",
"unix_file_perm": "",
"enable_h2c": false,
"enable_h3": false
},
"temp_dir": "/opt/openlist/data/temp",
"bleve_dir": "/opt/openlist/data/bleve",
"dist_dir": "",
"log": {
"enable": true,
"name": "/opt/openlist/data/log/log.log",
"max_size": 50,
"max_backups": 30,
"max_age": 28,
"compress": false,
"filter": {
"enable": false,
"filters": [
{ "cidr": "", "path": "/ping", "method": "" },
{ "cidr": "", "path": "", "method": "HEAD" },
{ "cidr": "", "path": "/dav/", "method": "PROPFIND" }
]
}
},
"delayed_start": 0,
"auto_memory_limit": 4,
"min_free_memory": 0,
"max_block_limit": 0,
"max_connections": 0,
"max_concurrency": 64,
"tls_insecure_skip_verify": false,
"tasks": {
"download": { "workers": 5, "max_retry": 1, "task_persistant": false },
"transfer": { "workers": 5, "max_retry": 2, "task_persistant": false },
"upload": { "workers": 5, "max_retry": 0, "task_persistant": false },
"copy": { "workers": 5, "max_retry": 2, "task_persistant": false },
"move": { "workers": 5, "max_retry": 2, "task_persistant": false },
"decompress": { "workers": 5, "max_retry": 2, "task_persistant": false },
"decompress_upload": { "workers": 5, "max_retry": 2, "task_persistant": false },
"allow_retry_canceled": false
},
"cors": { "allow_origins": ["*"], "allow_methods": ["*"], "allow_headers": ["*"] },
"s3": { "enable": false, "port": 5246, "ssl": false, "multipart_ttl": "24h" },
"ftp": {
"enable": false,
"listen": ":5221",
"find_pasv_port_attempts": 50,
"active_transfer_port_non_20": false,
"idle_timeout": 900,
"connection_timeout": 30,
"disable_active_mode": false,
"default_transfer_binary": false,
"enable_active_conn_ip_check": true,
"enable_pasv_conn_ip_check": true
},
"sftp": { "enable": false, "listen": ":5222" },
"mcp": { "enable": false },
"last_launched_version": "v4.2.9",
"proxy_address": ""
}
复现链接(可选)
No response
AI生成内容
- 我使用了AI工具生成此内容
- 我没有使用AI工具生成此内容
AI模型是
opus5.5 并有fable5.1和gpt-sol6.1独立二审
Activity