CodeQL 文档

可写文件句柄在没有错误处理的情况下关闭

ID: go/unhandled-writable-file-close
Kind: path-problem
Security severity: 
Severity: warning
Precision: high
Tags:
   - maintainability
   - correctness
   - call
   - defer
Query suites:
   - go-security-and-quality.qls

点击查看 CodeQL 存储库中的查询

写入文件句柄的数据可能不会被操作系统立即刷新到基础存储介质。通常情况下,数据可能会缓存在内存中,直到句柄关闭,或者在那之后的某个时间点。只有调用 os.File.Sync 才能合理地保证数据已刷新。因此,在调用 os.File.Closeos.File.Sync 之前,写入错误可能不会发生。如果调用了其中任何一个,并且它们返回的任何错误都被丢弃,那么程序可能不知道发生了数据丢失。

建议

始终检查 os.File.Close 是否返回错误,并进行适当处理。

示例

在第一个示例中,对 os.File.Close 进行了两次调用,目的是在所有文件操作完成后或写入文件失败时关闭文件。但是,虽然处理了调用 os.File.WriteString 期间可能出现的错误,但调用 os.File.Close 期间出现的任何错误都会被静默丢弃

package main

import (
	"os"
)

func example() error {
	f, err := os.OpenFile("file.txt", os.O_WRONLY|os.O_CREATE, 0666)

	if err != nil {
		return err
	}

	if _, err := f.WriteString("Hello"); err != nil {
		f.Close()
		return err
	}

	f.Close()

	return nil
}

在第二个示例中,为了实现与第一个示例相同的行为,对 os.File.Close 的调用被延迟了。但是,虽然处理了调用 os.File.WriteString 期间可能出现的错误,但 os.File.Close 出现的任何错误再次被静默丢弃

package main

import (
	"os"
)

func example() error {
	f, err := os.OpenFile("file.txt", os.O_WRONLY|os.O_CREATE, 0666)

	if err != nil {
		return err
	}

	defer f.Close()

	if _, err := f.WriteString("Hello"); err != nil {
		return err
	}

	return nil
}

要解决此问题,请显式处理调用 os.File.Close 时出现的错误

package main

import (
	"os"
)

func example() (exampleError error) {
	f, err := os.OpenFile("file.txt", os.O_WRONLY|os.O_CREATE, 0666)

	if err != nil {
		return err
	}

	defer func() {
		// try to close the file; if an error occurs, set the error returned by `example`
		// to that error, but only if `WriteString` didn't already set it to something first
		if err := f.Close(); exampleError == nil && err != nil {
			exampleError = err
		}
	}()

	if _, err := f.WriteString("Hello"); err != nil {
		exampleError = err
	}

	return
}

参考

  • ©GitHub 公司
  • 条款
  • 隐私