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
에 길이가 9자 이하인 null로 끝나는 문자열이 들어 있을 것으로 예상합니다. 하지만 I/O 오류가 발생하면 fgets()
는 buf
를 null로 종료하지 않습니다. 뿐만 아니라, 문자를 읽기 전에 파일 끝에 도달하면 fgets()
는 buf
에 아무것도 쓰지 않고 반환합니다. 두 가지 경우 모두, fgets()
는 NULL
을 반환하여 이상이 발생했음을 알리지만 이 경고는 인식되지 않습니다. buf
에 null 종결자가 없으면 이후의 strcpy()
호출에서 buffer overflow가 발생할 수 있습니다.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
은 다음 file system 함수의 첫 번째 매개 변수(버전 번호)에 전달해야 합니다.
__xmknod
2
는 다음 와이드 문자열 함수의 세 번째 매개 변수(그룹 인수)에 전달해야 합니다.
__wcstod_internal
__wcstof_internal
_wcstol_internal
__wcstold_internal
__wcstoul_internal
3
은 다음 file system 함수의 첫 번째 매개 변수(버전 번호)에 전달해야 합니다.
__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 패키지의 일부입니다. 반환된 작성자에 대한 각각의 쓰기는 지정된 우선 순위(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()
메서드가 localhost의 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()
메서드를 오버라이드하지 않거나 종료 시퀀스 중 올바로 릴리스하지도 않는 Android 작업을 설명합니다.
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()
메서드를 오버라이드하지 않거나 종료 시퀀스 중 올바로 릴리스하지도 않는 Android 작업을 설명합니다.
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();