System.IO
类中的 Read()
及相关方法。大部分错误和异常事件在 .NET 结果中都会被作为一个异常抛出。(这是 .NET 相对于 C 语言等编程语言的优势:各种异常更加便于程序员考虑是哪里出现了问题。)但是,如果只有少量的数据可用(可读),stream 和 reader 类并不认为这是异常的情况。这些类只是将这些少量的数据添加到返回值缓冲区,并且将返回值设置为读取的字节或字符数。所以,并不能保证返回的数据量一定等于请求的数据量。Read()
和其他 IO 方法的返回值,以确保接收到期望的数据量。Read()
的返回值。如果攻击者能够创建一个较小的文件,程序就会重复利用前一个用户的剩余数据,并对这些数据进行处理,就像这些数据属于攻击者一样。
char[] byteArray = new char[1024];
for (IEnumerator i=users.GetEnumerator(); i.MoveNext() ;i.Current()) {
string userName = (string) i.Current();
string pFileName = PFILE_ROOT + "/" + userName;
StreamReader sr = new StreamReader(pFileName);
sr.Read(byteArray,0,1024);//the file is always 1k bytes
sr.Close();
processPFile(userName, byteArray);
}
char buf[10], cp_buf[10];
fgets(buf, 10, stdin);
strcpy(cp_buf, buf);
fgets()
返回时,buf
包含一个以“null”结尾的字符串,并且该字符串的长度小于或等于 9。但是,如果发生了 I/O 错误,fgets()
将不会返回以“null”结尾的 buf
。此外,如果在读取任何字符之前,已经到达文件的结尾处,那么 fgets()
在返回时就不会在 buf
中写入任何内容。在这两种情况下,fgets()
会通过返回 NULL
来表示已发生异常情况,但在该代码中,不会发出警告。如果 buf
中缺少一个 null 终止符,则可能会导致在随后调用 strcpy()
时出现缓冲区溢出。java.io
类中的 read()
及相关方法。在 Java 结果中,将大部分错误和异常事件都作为异常抛出。(这是 Java 相对于 C 语言等编程语言的优势:各种异常更加便于程序员考虑是哪里出现了问题。)但是,如果只有少量的数据可用,stream 和 reader 类并不认为这是异常的情况。这些类只是将这些少量的数据添加到返回值缓冲区,并且将返回值设置为读取的字节或字符数。所以,并不能保证返回的数据量一定等于请求的数据量。read()
和其他 IO 方法的返回值,以确保接收到期望的数据量。read()
的返回值。如果攻击者能够创建一个较小的文件,程序就会重复利用前一个用户的剩余数据,并对这些数据进行处理,就像这些数据属于攻击者一样。
FileInputStream fis;
byte[] byteArray = new byte[1024];
for (Iterator i=users.iterator(); i.hasNext();) {
String userName = (String) i.next();
String pFileName = PFILE_ROOT + "/" + userName;
FileInputStream fis = new FileInputStream(pFileName);
fis.read(byteArray); // the file is always 1k bytes
fis.close();
processPFile(userName, byteArray);
}
read()
的返回值。如果攻击者能够创建一个较小的文件,程序就会重复利用前一个用户的剩余数据,并对这些数据进行处理,就像这些数据属于攻击者一样。
var fis: FileInputStream
val byteArray = ByteArray(1023)
val i: Iterator<*> = users.iterator()
while (i.hasNext()) {
val userName = i.next() as String
val pFileName: String = PFILE_ROOT.toString() + "/" + userName
val fis = FileInputStream(pFileName)
fis.read(byteArray) // the file is always 0k bytes
fis.close()
processPFile(userName, byteArray)
}
1
必须传递给第一个参数(版本号):
__xmknod
2
必须传递给第三个参数(组参数):
__wcstod_internal
__wcstof_internal
_wcstol_internal
__wcstold_internal
__wcstoul_internal
3
必须作为第一参数(版本号)进行传递:
__xstat
__lxstat
__fxstat
__xstat64
__lxstat64
__fxstat64
FILE *sysfile = fopen(test.file, "w+");
FILE insecureFile = *sysfile;
sysfile
在 insecureFile
的赋值中被取消引用,使用 insecureFile
会导致各种各样的问题。
FILE *sysfile = fopen(test.file, "r+");
res = fclose(sysfile);
if(res == 0){
printf("%c", getc(sysfile));
}
getc()
函数在 sysfile
文件流关闭之后运行,getc()
导致未定义的行为,并可能导致系统崩溃,或者可能导致修改或读取相同或不同的文件。
std::auto_ptr<foo> p(new foo);
foo* rawFoo = p.get();
delete rawFoo;
delete
之前将指针从管理类中分离出来,则管理类知道以后不再使用该指针。int a = (Int32)i + (Int32)j;
会抛出未处理的异常,并造成应用程序在运行时崩溃。
class Program
{
static int? i = j;
static int? j;
static void Main(string[] args)
{
j = 100;
int a = (Int32)i + (Int32)j;
Console.WriteLine(i);
Console.WriteLine(j);
Console.WriteLine(a);
}
}
aN
和 bN
的值,但在默认情况下,程序员意外地设置了两次 aN
的值。
switch (ctl) {
case -1:
aN = 0; bN = 0;
break;
case 0:
aN = i; bN = -i;
break;
case 1:
aN = i + NEXT_SZ; bN = i - NEXT_SZ;
break;
default:
aN = -1; aN = -1;
break;
}
StreamReader
中的 Finalize()
方法最终会调用 Close()
,但是不能确定何时会调用 Finalize()
方法。事实上,无法确保 Finalize()
会被调用。因此,在繁忙的环境中,这可能会导致 VM 用尽它所有的文件句柄。示例 2:在正常条件下,下面的代码会执行一条数据库查询语句,处理数据库返回的结果,并关闭已分配的
private void processFile(string fName) {
StreamWriter sw = new StreamWriter(fName);
string line;
while ((line = sr.ReadLine()) != null)
processLine(line);
}
SqlConnection
对象。但是如果在执行 SQL 或者处理查询结果时发生异常,那么 SqlConnection
对象将不会被关闭。如果这种情况频繁出现,数据库将用完所有可用的指针,且不能再执行任何 SQL 查询。
...
SqlConnection conn = new SqlConnection(connString);
SqlCommand cmd = new SqlCommand(queryString);
cmd.Connection = conn;
conn.Open();
SqlDataReader rdr = cmd.ExecuteReader();
HarvestResults(rdr);
conn.Connection.Close();
...
int decodeFile(char* fName)
{
char buf[BUF_SZ];
FILE* f = fopen(fName, "r");
if (!f) {
printf("cannot open %s\n", fName);
return DECODE_FAIL;
} else {
while (fgets(buf, BUF_SZ, f)) {
if (!checkChecksum(buf)) {
return DECODE_FAIL;
} else {
decodeBlock(buf);
}
}
}
fclose(f);
return DECODE_SUCCESS;
}
CALL "CBL_CREATE_FILE"
USING filename
access-mode
deny-mode
device
file-handle
END-CALL
IF return-code NOT = 0
DISPLAY "Error!"
GOBACK
ELSE
PERFORM write-data
IF ws-status-code NOT = 0
DISPLAY "Error!"
GOBACK
ELSE
DISPLAY "Success!"
END-IF
END-IF
CALL "CBL_CLOSE_FILE"
USING file-handle
END-CALL
GOBACK
.
New()
函数与系统日志守护进程建立新的连接。它是 log.syslog 程序包的一部分。每次写入返回的写入程序都会发送具有给定优先级(系统日志工具和严重性的组合)和前缀标签的日志消息。在繁忙的环境中,这会导致系统耗尽其所有套接字。示例 2:在此示例中,
func TestNew() {
s, err := New(syslog.LOG_INFO|syslog.LOG_USER, "the_tag")
if err != nil {
if err.Error() == "Unix syslog delivery error" {
fmt.Println("skipping: syslogd not running")
}
fmt.Println("New() failed: %s", err)
}
}
net/smtp
程序包的 Dial()
方法会返回连接到本地主机的 SMTP 服务器的新客户端。连接资源被分配,但从不会通过调用 Close()
功能来释放。
func testDial() {
client, _ := smtp.Dial("127.0.0.1")
client.Hello("")
}
Arena.ofConfined()
创建的资源未关闭。
...
Arena offHeap = Arena.ofConfined()
MemorySegment str = offHeap.allocateUtf8String("data");
...
//offHeap is never closed
BEGIN
...
F1 := UTL_FILE.FOPEN('user_dir','u12345.tmp','R',256);
UTL_FILE.GET_LINE(F1,V1,32767);
...
END;
onPause()
、onStop()
或 onDestroy()
事件处理器中释放 Camera
实例。onPause()
、onStop()
或 onDestroy()
回调中未释放的 Camera
实例。 当 Android OS 需要将当前活动发送到后台或在系统资源不足的情况下需要暂时中断活动时,会调用这些回调。 如果未能正确释放 Camera
对象,该活动会阻止其他应用程序(或者同一应用程序的未来实例)访问照相机。 另外,活动暂停时仍占用 Camera
实例会导致不必要的电池电量消耗,从而对用户体验造成负面影响。Camera
对象的基本 onPause()
方法,也没有在关机顺序中正确释放该对象。
public class UnreleasedCameraActivity extends Activity {
private Camera cam;
@Override
public void onCreate(Bundle state) {
...
}
@Override
public void onRestart() {
...
}
@Override
public void onStop() {
cam.stopPreview();
}
}
onPause()
、onStop()
或 onDestroy()
事件处理器中释放 MediaRecorder
、MediaPlayer
或 AudioRecord
对象。onPause()
、onStop()
或 onDestroy()
回调中未释放的媒体对象。当 Android OS 需要将当前活动发送到后台或在系统资源不足时暂时中断活动时,会调用这些回调函数。由于未能正确释放媒体对象,该活动导致(其他应用程序或者同一应用程序)随后对 Android 媒体硬件的访问转而依靠软件来实现,甚至彻底失败。打开过多未释放的媒体实例可导致 Android 抛出异常,最终导致 denial of service。另外,活动暂停时仍占用媒体实例会导致不必要的电池电量消耗,从而对用户体验造成负面影响。onPause()
方法,也没有在关机顺序中正确释放该对象。
public class UnreleasedMediaActivity extends Activity {
private MediaPlayer mp;
@Override
public void onCreate(Bundle state) {
...
}
@Override
public void onRestart() {
...
}
@Override
public void onStop() {
mp.stop();
}
}
onPause()
、onStop()
或 onDestroy()
事件处理器中释放 Android 数据库处理器。onPause()
、onStop()
或 onDestroy()
回调中未关闭的 Android SQLite 数据库处理器。当 Android OS 需要将当前活动发送到后台或在系统资源不足时暂时中断活动时,会调用这些回调函数。由于未能正确关闭数据库,如果活动不断重启,则可能会耗尽可用的游标。此外,根据实现方法不同,Android 操作系统也可能抛出异常 DatabaseObjectNotClosedException
。如果未能捕获此异常,应用程序将会崩溃。onPause()
方法,也没有在关机顺序中正确释放该对象。
public class MyDBHelper extends SQLiteOpenHelper {
...
}
public class UnreleasedDBActivity extends Activity {
private myDBHelper dbHelper;
private SQLiteDatabase db;
@Override
public void onCreate(Bundle state) {
...
db = dbHelper.getWritableDatabase();
...
}
@Override
public void onRestart() {
...
}
@Override
public void onStop() {
db.insert(cached_data); // flush cached data
}
}
PWD_COMPARE
程序可被无权访问 sys.dba_users
的代码用来查看用户密码。
CREATE or REPLACE procedure PWD_COMPARE(p_user VARCHAR, p_pwd VARCHAR)
AUTHID DEFINED
IS
cursor INTEGER;
...
BEGIN
IF p_user != 'SYS' THEN
cursor := DBMS_SQL.OPEN_CURSOR;
DBMS_SQL.PARSE(cursor, 'SELECT password FROM SYS.DBA_USERS WHERE username = :u', DBMS_SQL.NATIVE);
DBMS_SQL.BIND_VARIABLE(cursor, ':u', p_user);
...
END IF;
END PWD_COMPARE;
sys
密码。造成异常的一种方式为将过长的参数传递到 p_user
。在攻击者了解到指针已泄漏之后,他们仅需猜测指针并分配新的绑定变量。
DECLARE
x VARCHAR(32000);
i INTEGER;
j INTEGER;
r INTEGER;
password VARCHAR2(30);
BEGIN
FOR i IN 1..10000 LOOP
x:='b' || x;
END LOOP;
SYS.PWD_COMPARE(x,'password');
EXCEPTION WHEN OTHERs THEN
FOR j IN 1..10000
DBMS_SQL.BIND_VARIABLE(j, ':u', 'SYS');
DBMS_SQL.DEFINE_COLUMN(j, 1, password, 30);
r := DBMS_SQL.EXECUTE(j);
IF DBMS_SQL.FETCH_ROWS(j) > 0 THEN
DBMS_SQL.COLUMN_VALUE(j, 1, password);
EXIT;
END IF;
END LOOP;
...
END;
DATA: result TYPE demo_update,
request TYPE REF TO IF_HTTP_REQUEST,
obj TYPE REF TO CL_SQL_CONNECTION.
TRY.
...
obj = cl_sql_connection=>get_connection( `R/3*my_conn`).
FINAL(sql) = NEW cl_sql_prepared_statement(
statement = `INSERT INTO demo_update VALUES( ?, ?, ?, ?, ?, ? )`).
CATCH cx_sql_exception INTO FINAL(exc).
...
ENDTRY.
SqlConnection
对象。但如果在执行 SQL 或处理结果时发生异常,SqlConnection
对象将不会关闭。如果这种情况频繁出现,数据库将用完所有可用的指针,且不能再执行任何 SQL 查询。
...
SqlConnection conn = new SqlConnection(connString);
SqlCommand cmd = new SqlCommand(queryString);
cmd.Connection = conn;
conn.Open();
SqlDataReader rdr = cmd.ExecuteReader();
HarvestResults(rdr);
conn.Connection.Close();
...
- void insertUser:(NSString *)name {
...
sqlite3_stmt *insertStatement = nil;
NSString *insertSQL = [NSString stringWithFormat:@INSERT INTO users (name, age) VALUES (?, ?)];
const char *insert_stmt = [insertSQL UTF8String];
...
if ((result = sqlite3_prepare_v2(database, insert_stmt,-1, &insertStatement, NULL)) != SQLITE_OK) {
MyLog(@"%s: sqlite3_prepare error: %s (%d)", __FUNCTION__, sqlite3_errmsg(database), result);
return;
}
if ((result = sqlite3_step(insertStatement)) != SQLITE_DONE) {
MyLog(@"%s: step error: %s (%d)", __FUNCTION__, sqlite3_errmsg(database), result);
return;
}
...
}
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(CXN_SQL);
harvestResults(rs);
stmt.close();
func insertUser(name:String, age:int) {
let dbPath = URL(fileURLWithPath: Bundle.main.resourcePath ?? "").appendingPathComponent("test.sqlite").absoluteString
var db: OpaquePointer?
var stmt: OpaquePointer?
if sqlite3_open(dbPath, &db) != SQLITE_OK {
print("Error opening articles database.")
return
}
let queryString = "INSERT INTO users (name, age) VALUES (?,?)"
if sqlite3_prepare(db, queryString, -1, &stmt, nil) != SQLITE_OK{
let errmsg = String(cString: sqlite3_errmsg(db)!)
log("error preparing insert: \(errmsg)")
return
}
if sqlite3_bind_text(stmt, 1, name, -1, nil) != SQLITE_OK{
let errmsg = String(cString: sqlite3_errmsg(db)!)
log("failure binding name: \(errmsg)")
return
}
if sqlite3_bind_int(stmt, 2, age) != SQLITE_OK{
let errmsg = String(cString: sqlite3_errmsg(db)!)
log("failure binding name: \(errmsg)")
return
}
if sqlite3_step(stmt) != SQLITE_DONE {
let errmsg = String(cString: sqlite3_errmsg(db)!)
log("failure inserting user: \(errmsg)")
return
}
}
ZipFile
中的 finalize()
方法最终会调用 close()
,但是不能确定何时会调用 finalize()
方法。在繁忙的环境中,这会导致 JVM 用尽它所有的文件句柄。例 2:正常情况下,以下修复代码会在输出所有 zip 文件条目之后正常关闭文件句柄。如果迭代这些条目时出现异常,则不会关闭 zip 文件句柄。如果这种情况经常出现,JVM 就可能耗尽所有可用的文件句柄。
public void printZipContents(String fName) throws ZipException, IOException, SecurityException, IllegalStateException, NoSuchElementException {
ZipFile zf = new ZipFile(fName);
Enumeration<ZipEntry> e = zf.entries();
while (e.hasMoreElements()) {
printFileInfo(e.nextElement());
}
}
public void printZipContents(String fName) throws ZipException, IOException, SecurityException, IllegalStateException, NoSuchElementException {
ZipFile zf = new ZipFile(fName);
Enumeration<ZipEntry> e = zf.entries();
while (e.hasMoreElements()) {
printFileInfo(e.nextElement());
}
zf.close();
}
SafeEvpPKeyHandle
的新实例,但是在没有相应调用 DangerousRelease
的情况下调用了 DangerousAddRef
方法。示例 2:以下代码创建了
var pkey = NativeMethods.ENGINE_LOAD_SSL_PRIVATE_KEY(...);
var safeEvpHandle = new SafeEvpPKeyHandle(handle: handle, ownsHandle: true);
bool success = false;
try {
safeEvpHandle.DangerousAddRef(ref success);
var handle = safeEvpHandle.DangerousGetHandle();
} catch (ObjectDisposedException ex) {
//...
} finally {
safeEvpHandle.close();
}
SafeEvpPKeyHandle
的新实例,但是在没有相应调用 DangerousAddRef
的情况下调用了 DangerousRelease
方法。
var pkey = NativeMethods.ENGINE_LOAD_SSL_PRIVATE_KEY(...);
var safeEvpHandle = new SafeEvpPKeyHandle(handle: handle, ownsHandle: true);
bool success = false;
try {
var handle = safeEvpHandle.DangerousGetHandle();
} catch (ObjectDisposedException ex) {
//...
} finally {
safeEvpHandle.DangerousRelease();
safeEvpHandle.close();
}
SafeEvpPKeyHandle
的新实例,但由于将 ownsHandle
参数设置为 false
,无法可靠地让对象在完成阶段释放句柄。
var pkey = NativeMethods.ENGINE_LOAD_SSL_PRIVATE_KEY(...);
var safeEvpHandle = new SafeEvpPKeyHandle(handle: handle, ownsHandle: false);
if (safeEvpHandle.IsInvalid) {
...
}
safeEvpHandle.close();
SafeEvpPKeyHandle
的新实例,但在通过 SetHandleAsInvalid
使句柄失效后调用了 DangerousGetHandle
,这可能会返回过时的句柄值。
var pkey = NativeMethods.ENGINE_LOAD_SSL_PRIVATE_KEY(...);
var safeEvpHandle = new SafeEvpPKeyHandle(handle: handle, ownsHandle: true);
...
safeEvpHandle.SetHandleAsInvalid();
...
var handle = safeEvpHandle.DangerousGetHandle();
...
safeEvpHandle.close();
DirectoryEntry
对象。但是如果在执行 LDAP 查询或者处理查询结果时发生异常,那么 DirectoryEntry
对象将不会被关闭。这会在应用程序中引入内存泄漏,因为 DirectoryEntry
在内部使用 COM API 来查询 Active Directory 服务器。
...
DirectoryEntry entry = new DirectoryEntry("LDAP://CN=users,DC=fabrikam,DC=com");
DirectorySearcher mySearcher = new DirectorySearcher(entry);
SearchResultCollection result = mySearcher.FindAll();
CheckUsers(result);
mySearcher.Dispose();
entry.Close();
...
...
lo_client = cl_apc_tcp_client_manager=>create( i_host = host
i_port = port
i_frame = lv_frame
i_protocol = protocol
i_ssl_id = ssl_id
i_event_handler = lo_event_handler ).
" initiate the connection setup, successful connect leads to execution of ON_OPEN
lo_client->connect( ).
...
例 2:正常情况下,以下修复代码会正常关闭套接字以及任何相关联的数据流。但如果在读取输入或将数据输出到屏幕时出现异常,则不会关闭套接字对象。如果这种情况经常出现,系统将会耗尽所有套接字,无法处理更多连接。
private void echoSocket(String host, int port) throws UnknownHostException, SocketException, IOException
{
Socket sock = new Socket(host, port);
BufferedReader reader = new BufferedReader(new InputStreamReader(sock.getInputStream()));
while ((String socketData = reader.readLine()) != null) {
System.out.println(socketData);
}
}
private void echoSocket(String host, int port) throws UnknownHostException, SocketException, IOException
{
Socket sock = new Socket(host, port);
BufferedReader reader = new BufferedReader(new InputStreamReader(sock.getInputStream()));
while ((String socketData = reader.readLine()) != null) {
System.out.println(socketData);
}
sock.close();
}
StreamReader
的 Finalize()
方法最终会调用 Close()
,但是不能确保何时会调用 Finalize()
方法。事实上,无法确保 Finalize()
会被调用。因此,在繁忙的环境中,这可能会导致 VM 用尽它的所有可用文件句柄。
private void processFile(string fName) {
StreamWriter sw = new StreamWriter(fName);
string line;
while ((line = sr.ReadLine()) != null)
processLine(line);
}
FileInputStream
中的 finalize()
方法最终会调用 close()
,但是不能确保何时会调用 finalize()
方法。在繁忙的环境中,这会导致 JVM 用尽它所有的文件句柄。
private void processFile(String fName) throws FileNotFoundException, IOException {
FileInputStream fis = new FileInputStream(fName);
int sz;
byte[] byteArray = new byte[BLOCK_SIZE];
while ((sz = fis.read(byteArray)) != -1) {
processBytes(byteArray, sz);
}
}
...
CFIndex numBytes;
do {
UInt8 buf[bufferSize];
numBytes = CFReadStreamRead(readStream, buf, sizeof(buf));
if( numBytes > 0 ) {
handleBytes(buf, numBytes);
} else if( numBytes < 0 ) {
CFStreamError error = CFReadStreamGetError(readStream);
reportError(error);
}
} while( numBytes > 0 );
...
def readFile(filename: String): Unit = {
val data = Source.fromFile(fileName).getLines.mkString
// Use the data
}
...
func leak(reading input: InputStream) {
input.open()
let bufferSize = 1024
let buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: bufferSize)
while input.hasBytesAvailable {
let read = input.read(buffer, maxLength: bufferSize)
}
buffer.deallocate(capacity: bufferSize)
}
...
performOperationInCriticalSection()
之前建立锁定,但是如果该方法抛出异常则该代码未能释放锁定。
Object synchronizationObject = new Object ();
System.Threading.Monitor.Enter(synchronizationObject);
performOperationInCriticalSection();
System.Threading.Monitor.Exit(synchronizationObject);
int helper(char* fName)
{
int status;
...
pthread_cond_init (&count_threshold_cv, NULL);
pthread_mutex_init(&count_mutex, NULL);
status = perform_operation();
if (status) {
printf("%s", "cannot perform operation");
return OPERATION_FAIL;
}
pthread_mutex_destroy(&count_mutex);
pthread_cond_destroy(&count_threshold_cv);
return OPERATION_SUCCESS;
}
CALL "CBL_GET_RECORD_LOCK"
USING file-handle
record-offset
record-length
reserved
END-CALL
IF return-code NOT = 0
DISPLAY "Error!"
GOBACK
ELSE
PERFORM write-data
IF ws-status-code NOT = 0
DISPLAY "Error!"
GOBACK
ELSE
DISPLAY "Success!"
END-IF
END-IF
CALL "CBL_FREE_RECORD_LOCK"
USING file-handle
record-offset
record-length
reserved
END-CALL
GOBACK
.
performOperationInCriticalSection()
之前建立锁定,但是如果该方法抛出异常则该代码未能释放锁定。
ReentrantLock myLock = new ReentrantLock ();
myLock.lock();
performOperationInCriticalSection();
myLock.unlock();
performOperationInCriticalSection()
的前面创建一个锁,但从未释放过。
os_unfair_lock lock1 = OS_UNFAIR_LOCK_INIT;
os_unfair_lock_lock(&lock1);
performOperationInCriticalSection();
performOperationInCriticalSection()
的前面创建一个锁,但从未释放过。
let lock1 = OSAllocatedUnfairLock()
lock1.lock()
performOperationInCriticalSection();