코드 품질이 낮으면 예측할 수 없는 동작이 발생합니다. 사용자 입장에서는 사용 편의성이 떨어지는 것으로 나타나는 경우가 많습니다. 공격자에게는 예상치 못한 방법으로 시스템에 부담을 줄 수 있는 기회가 됩니다.
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();
incomingStream
에서 관리 Bitmap Object를 생성합니다. Bitmap은 발신 스트림 outgoingStream
으로 조작 및 지속됩니다. incomingBitmap
및 outgoingBitmap
의 Dispose()
메서드는 명시적으로 호출되지 않습니다.Bitmap.Dispose()
를 호출합니다. 그러나, Bitmap
개체는 부족한 비관리 시스템 리소스를 이용합니다. Garbage Collector는 비관리 리소스 풀이 고갈되기 전에 Dispose()
를 호출하지 못할 수도 있습니다.
private void processBitmap(Stream incomingStream, Stream outgoingStream, int thumbnailSize)
{
Bitmap incomingBitmap = (Bitmap)System.Drawing.Image.FromStream(incomingStream);
bool validBitmap = validateBitmap(incomingBitmap);
if (!validBitmap)
throw new ValidationException(incomingBitmap);
Bitmap outgoingBitmap = new Bitmap(incomingBitmap, new Size(thumbnailSize, thumbnailSize));
outgoingBitmap.Save(outgoingStream, ImageFormat.Bmp);
}
char* ptr = (char*)malloc (SIZE);
...
if (err) {
abrt = 1;
free(ptr);
}
...
if (abrt) {
logError("operation aborted before commit", ptr);
}