I wrote a kernel module that needs to push messages to user space. The idea is that the kernel module buffers the message and signals the user space program, then the user space program goes and gets the message by requesting it over a netlink socket. My problem is that after buffering 90 messages, the machine locks and I need to restart. I can't figure out what I'm doing wrong, and I'm using linked lists elsewhere in the kernel module successfully.
//
// A message from the kernel space to user space.
//
typedef struct CoreLinkMessage
{
unsigned int id;
char* data;
unsigned int length;
struct list_head list; // kernel's list structure
} CoreLinkMessage;
This function initializes the list and semaphore:
// Constructor
void
ctsRtNetlinkSystem_init( void )
{
sema_init(&cmd_sem_, 1);
INIT_LIST_HEAD(&cmd_list_.list);
}
This is the function that must be causing the problem. It simply pushes an item on to the tail of the linked list. If I comment out adding items to the linked list and only call a signal, the program runs indefinitely, so I don't think the problem is the signaling.
//
// Allows the kernel module to buffer messages until requested by
// the user space
//
void
ctsRtNetlinkSystem_addMessage(char* data, unsigned int length)
{
CoreLinkMessage* msg;
int sem_ret;
BOOL doSignal = FALSE;
//
// LOCK the semaphore
//
sem_ret = down_interruptible(&cmd_sem_);
if ( !sem_ret )
{
msg = (CoreLinkMessage*)kmalloc(sizeof(CoreLinkMessage), GFP_KERNEL );
if ( msg == NULL )
{
PRINTF(CTSMSG_INFO
"ctsRtNetlinkSystem_addMessage failed to allocate memory! \n" );
goto unlock;
}
memset( msg, 0, sizeof(CoreLinkMessage) );
msg->data = (char*)kmalloc( length, GFP_KERNEL );
if ( msg->data == NULL )
{
kfree( msg );
PRINTF(CTSMSG_INFO
"ctsRtNetlinkSystem_addMessage failed to allocate data memory!\n" );
goto unlock;
}
memcpy( msg->data, data, length );
msg->length = length;
lastMessageId_ += 1;
msg->id = lastMessageId_;
list_add_tail(&(msg->list), &cmd_list_.list);
doSignal = TRUE;
unlock:
up( &cmd_sem_ );
if ( doSignal )
sendMessageSignal( msg->id );
}
else
{
PRINTF(CTSMSG_INFO
"CtsRtNetlinkSystem_addMessage -- failed to get semaphore\n" );
}
}
//
// Signal the user space that a message is waiting. Pass along the message
// id
//
static BOOL
sendMessageSignal( unsigned int id )
{
int ret;
struct siginfo info;
struct task_struct *t;
memset(&info, 0, sizeof(struct siginfo));
info.si_signo = SIGNAL_MESSAGE;
info.si_code = SI_QUEUE; // this is bit of a trickery:
// SI_QUEUE is normally used by sigqueue
// from user space,
// and kernel space should use SI_KERNEL.
// But if SI_KERNEL is used the real_time data
// is not delivered to the user space signal
// handler function.
// tell the user space application the index of the message
// real time signals may have 32 bits of data.
info.si_int = id;
rcu_read_lock();
//find the task_struct associated with this pid
t = // find_task_by_pid_type( PIDTYPE_PID, registeredPid_ );
// find_task_by_pid_type_ns(PIDTYPE_PID, nr, &init_pid_ns);
pid_task(find_vpid(registeredPid_), PIDTYPE_PID);
if(t == NULL)
{
PRINTF(CTSMSG_INFO
"CtsRtNetlinkSystem::sendMessageSignal -- no such pid\n");
rcu_read_unlock();
registeredPid_ = 0;
return FALSE;
}
rcu_read_unlock();
//send the signal
ret = send_sig_info(SIGNAL_MESSAGE, &info, t);
if (ret < 0)
{
PRINTF(CTSMSG_INFO
"CtsRtNetlinkSystem::sendMessageSignal -- \n"
"\t error sending signal %d \n", ret );
return FALSE;
}
return TRUE;
}
I'm currently testing the program on a VM, so I created a timer that ticks every 7 seconds and adds a message to the buffer.
//
// Create a timer to call the process thread
// with nanosecond resolution.
//
static void
createTimer(void)
{
hrtimer_init(
&processTimer_, // instance of process timer
CLOCK_MONOTONIC, // Pick a specific clock. CLOCK_MONOTONIC is
// guaranteed to move forward, no matter what.
// It's akin to jiffies tick count
// CLOCK_REALTIME matches the current real-world time
HRTIMER_MODE_REL ); // Timer mode (HRTIMER_ABS or HRTIMER_REL)
processTimer_.function = &cyclic_task;
processTimerNs_ = ktime_set(1, FREQUENCY_NSEC);
//
// Start the timer. It will callback the .function
// when the timer expires.
//
hrtimer_start(
&processTimer_, // instance of process timer
processTimerNs_, // time, nanosecconds
HRTIMER_MODE_REL ); // HRTIMER_REL indicates that time should be
// interpreted relative
// HRTIMER_ABS indicates time is an
// absolute value
}
static enum hrtimer_restart
cyclic_task(struct hrtimer* timer)
{
char msg[255];
sprintf(msg, "%s", "Testing the buffer.");
ctsRtNetlink_send( &msg[0], strlen(msg) );
hrtimer_forward_now(
&processTimer_,
processTimerNs_ );
return HRTIMER_RESTART;
}
Thanks in advance for any help.
Though your code flow is not very clear from the question, I feel the list addition may not be the problem. You must have the list handling elsewhere, where you must be removing the messages from the list etc. I suspect some sort of a deadlock situation somewhere between your list addition and removal etc. Also, check the place where you are copying the messages to the userspace and removing from the list and freeing it up. I suppose, you are not trying to directly referring your mesg from userspace as one of the commentator suggested above.
Also,
memset( msg, 0, sizeof(CoreLinkMessage) );
if ( msg == NULL )
{
These two lines has to reverse its order else, if alloc has failed your system is doomed.
Using GFP_ATOMIC instead of GFP_KERNEL for kmalloc solved the problem. Three days of run-time so far, and no crashing. I suspect one cannot sleep in a thread triggered by an hrtimer.
msg = (CoreLinkMessage*)kmalloc(sizeof(CoreLinkMessage), GFP_ATOMIC );
Thanks everyone for your insights!
Insufficient memory allocated
Be sure to allocate enough memory for the string length + 1 to store it's terminator.
In sending, an length + 1 may be needed.
// ctsRtNetlink_send( &msg[0], strlen(msg) );
ctsRtNetlink_send( &msg[0], strlen(msg) + 1); // +1 for \0
Related
I'm trying to pick up C, using an esp32. While looking at exactly how FreeRTOS works, I found the following page regarding how to use the tasks, and best practices etc.
https://www.freertos.org/implementing-a-FreeRTOS-task.html
According to this page, to prevent starvation, tasks should be event based. Regarding what I am trying to achieve, I will try to provide a very simplified example.
Background
I have a LCD screen, which should display data from a sensor. The data shown on the LCD will be done using a task, which according to the documentation, should never exit and should be event driven to prevent starvation.
I have a way of controlling the data shown on the LCD screen, which would be a rotary encoder. This encoder can be clicked, which should refresh the sensor's data.
Question
How would I implement the event based tasks, which are described on the FreeRTOS page, in this specific context? I had a look at the documentation and the "simple" example projects on their github, but as a beginner within C and embedded, they were extremely overwhelming.
Simple demo code
void update_sensor_task(void *pvParameters)
{
// Ensure the task keeps on running
for( ; ; )
{
if(event_update_sensor) // How would I be able to notify the task that this should be run?
{
// update the data
}
}
// Tasks should not be returning, but if they happen to do so, ensure a clean exit
vTaskDelete(NULL);
}
void screen_temperature_task(void *pvParameters)
{
for(; ;)
{
if(event_sensor_updated)
{
// Update the lcd screen with the new data
}
}
vTaskDelete(NULL);
}
void on_rotary_clicked(void *pvParameters)
{
// Notify the sensor task that it should be updating?
}
EDIT:
By using what has been marked as the correct answer, I have managed to get it to work by implementing it the following way:
/* Queue used to send and receive the data */
QueueHandle_t xStructQueue = NULL;
/* Struct which shall be used to hold and pass around the data for the LCD screen*/
struct LcdData
{
int current_temp;
int current_humidity;
} xLcdData;
void initialize_queues(void)
{
xLcdData.current_humidity = 0;
xLcdData.current_temp = 0;
xStructQueue = xQueueCreate(
/* The maximum number of items the queue can hold*/
5,
/* The size of each struct, which the queue should be able to hold */
sizeof( xLcdData )
);
if(xStructQueue == NULL)
{
ESP_LOGE(TAG, "Queue has not been initialized successfully");
}
}
void screen_temperature_task_simplified(void *pvParameters)
{
int counter = 0;
for(; ;)
{
struct LcdData xReceivedStructure;
BaseType_t result;
result = xQueueReceive(xStructQueue, &xReceivedStructure, ( TickType_t ) 10);
if(result == pdPASS)
{
counter = counter + 1;
char snum_current_counter[12];
sprintf(snum_current_counter, "%d", counter);
i2c_lcd1602_clear (lcd_info);
i2c_lcd1602_write_string (lcd_info, snum_current_counter);
}
}
vTaskDelete(NULL);
}
void update_sensor_struct(void)
{
xLcdData.current_temp = DHT11_read().temperature;
xLcdData.current_humidity = DHT11_read().humidity;
// Log the results in the console
printf("Temperature is %d \n", xLcdData.current_temp);
printf("Humidity is %d\n", xLcdData.current_humidity);
ESP_LOGI(TAG, "Data has been updated");
}
void on_rotary_clicked_simplified()
{
ESP_LOGI(TAG, "Rotary encoder has been clicked!");
// Update the struct which holds the data
update_sensor_struct();
/* Send the entire struct to the queue */
xQueueSend(
/* The handle of the queue */
xStructQueue,
/* The adress of the struct which should be sent */
(void *) &xLcdData,
/* Block time of 0 says don't block if the queue is already full.
Check the value returned by xQueueSend() to know if the message
was sent to the queue successfully. */
( TickType_t ) 0
);
}
I use FRTOS and event driven development.
The typical flow here would be:
for(;;)
{
BaseType_t result;
result = xQueueReceive(LCD_Event_Queue, &someLCDEvent, QUEUE_TIMEOUT);
if (result == pdPASS)
{
/* We have new event data in someLCDEvent; Use that data to update the LCD */
}
else
{
/* No new event, do some brief idle-time processing if necessary */
}
}
In brief, wait up to QUEUE_TIMEOUT time for a new event to arrive.
If a new event arrives within that timeframe successfully, then process the data in that event and update your screen.
If a new event does not arrive, you have an opportunity to do some other maintenance work.
Designing and defining the structure-type of someLCDEvent, and putting data into the queue is a big topic, and will depend a lot on your specific project.
My situation is that MmCopyVirtualMemory almost always (%99 of the time) returns STATUS_PARTIAL_COPY.
(Im operating in a Ring0 Driver)
I've tried so many different things, like using different variables sizes and types, different addresses etc... It always returns STATUS_PARTIAL_COPY.
Nothing online has helped either, its not a really common error.
Error description:
{Partial Copy} Due to protection conflicts not all the requested bytes could be copied.
My way of reading a processes memory:
DWORD64 Read(DWORD64 SourceAddress, SIZE_T Size)
{
SIZE_T Bytes;
NTSTATUS Status = STATUS_SUCCESS;
DWORD64 TempRead;
DbgPrintEx(0, 0, "\nRead Address:%p\n", SourceAddress); // Always Prints Correct Address
DbgPrintEx(0, 0, "Read szAddress:%x\n", Size); // Prints Correct Size 8 bytes
Status = MmCopyVirtualMemory(Process, SourceAddress, PsGetCurrentProcess(), &TempRead, Size, KernelMode, &Bytes);
DbgPrintEx(0, 0, "Read Bytes:%x\n", Bytes); // Copied bytes - prints 0
DbgPrintEx(0, 0, "Read Output:%p\n", TempRead); // prints 0 as expected since it failed
if (!NT_SUCCESS(Status))
{
DbgPrintEx(0, 0, "Read Failed:%p\n", Status);
return NULL;
}
return TempRead;
}
Example of how I use it:
NetMan = Read(BaseAddr + NET_MAN, sizeof(DWORD64));
//BaseAddr is a DWORD64 and NetMan is also a DWORD64
I've tripped checked my code to many times, all of it appears to be right.
After investigating MiDoPoolCopy (MmCopyVirtualMemoy calls this) it seems that its failing during the move operation:
// MiDoPoolCopy function
// Probe to make sure that the specified buffer is accessible in
// the target process.
//Wont execute (supplied KernelMode)
if ((InVa == FromAddress) && (PreviousMode != KernelMode)){
Probing = TRUE;
ProbeForRead (FromAddress, BufferSize, sizeof(CHAR));
Probing = FALSE;
}
//Failing either here, copying inside the target process's address space to the buffer
RtlCopyMemory (PoolArea, InVa, AmountToMove);
KeUnstackDetachProcess (&ApcState);
KeStackAttachProcess (&ToProcess->Pcb, &ApcState);
//
// Now operating in the context of the ToProcess.
//
//Wont execute (supplied KernelMode)
if ((InVa == FromAddress) && (PreviousMode != KernelMode)){
Probing = TRUE;
ProbeForWrite (ToAddress, BufferSize, sizeof(CHAR));
Probing = FALSE;
}
Moving = TRUE;
//or failing here - moving from the Target Process to Source (target process->Kernel)
RtlCopyMemory (OutVa, PoolArea, AmountToMove);
Here's the SEH returning STATUS_PARTIAL_COPY (wrapped in try and except)
//(wrapped in try and except)
//
// If the failure occurred during the move operation, determine
// which move failed, and calculate the number of bytes
// actually moved.
//
*NumberOfBytesRead = BufferSize - LeftToMove;
if (Moving == TRUE) {
//
// The failure occurred writing the data.
//
if (ExceptionAddressConfirmed == TRUE) {
*NumberOfBytesRead = (SIZE_T)((ULONG_PTR)(BadVa - (ULONG_PTR)FromAddress));
}
}
return STATUS_PARTIAL_COPY;
MmCopyVirtualMemory (undocumented struct)
NTSTATUS NTAPI MmCopyVirtualMemory
(
PEPROCESS SourceProcess,
PVOID SourceAddress,
PEPROCESS TargetProcess,
PVOID TargetAddress,
SIZE_T BufferSize,
KPROCESSOR_MODE PreviousMode,
PSIZE_T ReturnSize
);
Here is the source for both MmCopyVirtualMemory and MiDoPoolCopy:
https://lacicloud.net/custom/open/leaks/Windows%20Leaked%20Source/wrk-v1.2/base/ntos/mm/readwrt.c
Any help would be greatly appreciated, I've been stuck on this for to long...
I know this is old, but because I had the same issue and fixed it, maybe someone else will stumble on this in the future.
Reading your code the issue is the target address you want to copy the data. You are using a single defined DWORD64, which is when running the function a simple variable (register or on stack) and not a memory region where you can write to.
The solution is to provide a correct buffer for which you allocated memory before with malloc.
Example (you want to read a DWORD64, based on your code with some adjustments):
DWORD64* buffer = malloc(sizeof(DWORD64))
Status = MmCopyVirtualMemory(Process, (void*)SourceAddress, PsGetCurrentProcess(), (void*)buffer, sizeof(DWORD64), UserMode, &Bytes);
Do not forget to free your buffer after using to prevent a memory leak.
I'm a beginner to WDD. I need to make a network driver filter that drops certain packets based on the signature to protect system from program vulnerability.
Right now, I'm just trying to get the packets I need and find my signature. I use Metasploit Framework to exploit this vulnerability and Wireshark to track the traffic. In Wireshark I see 4 packets with destination port 8000 like this:
But when I'm trying to get them I only have 2 first of them - with SYN and ACK.
My code is based on NDIS driver filter sample. I'm writing inside the FilterReceiveNetBufferLists function. Here's the code:
_Use_decl_annotations_
VOID
FilterReceiveNetBufferLists(
NDIS_HANDLE FilterModuleContext,
PNET_BUFFER_LIST NetBufferLists,
NDIS_PORT_NUMBER PortNumber,
ULONG NumberOfNetBufferLists,
ULONG ReceiveFlags
)
/*++
Routine Description:
FilerReceiveNetBufferLists is an optional function for filter drivers.
If provided, this function processes receive indications made by underlying
NIC or lower level filter drivers. This function can also be called as a
result of loopback. If this handler is NULL, NDIS will skip calling this
filter when processing a receive indication and will call the next higher
driver in the stack. A filter that doesn't provide a
FilterReceiveNetBufferLists handler cannot provide a
FilterReturnNetBufferLists handler and cannot a initiate an original receive
indication on its own.
Arguments:
FilterModuleContext - our filter context area.
NetBufferLists - a linked list of NetBufferLists
PortNumber - Port on which the receive is indicated
ReceiveFlags -
N.B.: It is important to check the ReceiveFlags in NDIS_TEST_RECEIVE_CANNOT_PEND.
This controls whether the receive indication is an synchronous or
asynchronous function call.
--*/
{
PMS_FILTER pFilter = (PMS_FILTER)FilterModuleContext;
BOOLEAN DispatchLevel;
ULONG Ref;
BOOLEAN bFalse = FALSE;
#if DBG
ULONG ReturnFlags;
#endif
DEBUGP(DL_TRACE, "===>ReceiveNetBufferList: NetBufferLists = %p.\n", NetBufferLists);
do
{
DispatchLevel = NDIS_TEST_RECEIVE_AT_DISPATCH_LEVEL(ReceiveFlags);
#if DBG
FILTER_ACQUIRE_LOCK(&pFilter->Lock, DispatchLevel);
if (pFilter->State != FilterRunning)
{
FILTER_RELEASE_LOCK(&pFilter->Lock, DispatchLevel);
if (NDIS_TEST_RECEIVE_CAN_PEND(ReceiveFlags))
{
ReturnFlags = 0;
if (NDIS_TEST_RECEIVE_AT_DISPATCH_LEVEL(ReceiveFlags))
{
NDIS_SET_RETURN_FLAG(ReturnFlags, NDIS_RETURN_FLAGS_DISPATCH_LEVEL);
}
NdisFReturnNetBufferLists(pFilter->FilterHandle, NetBufferLists, ReturnFlags);
}
break;
}
FILTER_RELEASE_LOCK(&pFilter->Lock, DispatchLevel);
#endif
ASSERT(NumberOfNetBufferLists >= 1);
////////////////////////////// MY CODE //////////////////////////////////////
FILTER_ACQUIRE_LOCK(&pFilter->Lock, DispatchLevel);
PNET_BUFFER_LIST pCurrentNetBufferList = NetBufferLists;
PNET_BUFFER pCurrentNetBuffer = NET_BUFFER_LIST_FIRST_NB(pCurrentNetBufferList);
while (pCurrentNetBufferList)
{
while (pCurrentNetBuffer)
{
ULONG netbuffer_size = NET_BUFFER_DATA_LENGTH(pCurrentNetBuffer);
BYTE* buffer = (BYTE*)NdisGetDataBuffer(
pCurrentNetBuffer,
netbuffer_size,
NULL,
1,
0);
if (buffer) {
ParseBuffer(buffer, netbuffer_size);
}
else
{
void* alloc_mem = ExAllocatePoolWithTag(PagedPool, netbuffer_size, 'BteN');
buffer = (BYTE*)NdisGetDataBuffer(
pCurrentNetBuffer,
netbuffer_size,
alloc_mem,
1,
0);
if (buffer) {
ParseBuffer(buffer, netbuffer_size);
}
else {
DbgPrint("T.T");
}
ExFreePoolWithTag(alloc_mem, 'BteN');
}
pCurrentNetBuffer = NET_BUFFER_NEXT_NB(pCurrentNetBuffer);
}
pCurrentNetBufferList = NET_BUFFER_LIST_NEXT_NBL(pCurrentNetBufferList);
}
FILTER_RELEASE_LOCK(&pFilter->Lock, DispatchLevel);
////////////////////////////////////////////////////////////////////////////
//
// If you would like to drop a received packet, then you must carefully
// modify the NBL chain as follows:
//
// if NDIS_TEST_RECEIVE_CANNOT_PEND(ReceiveFlags):
// For each NBL that is NOT dropped, temporarily unlink it from
// the linked list, and indicate it up alone with
// NdisFIndicateReceiveNetBufferLists and the
// NDIS_RECEIVE_FLAGS_RESOURCES flag set. Then immediately
// relink the NBL back into the chain. When all NBLs have been
// indicated up, you may return from this function.
// otherwise (NDIS_TEST_RECEIVE_CANNOT_PEND is FALSE):
// Divide the linked list of NBLs into two chains: one chain
// of packets to drop, and everything else in another chain.
// Return the first chain with NdisFReturnNetBufferLists, and
// indicate up the rest with NdisFIndicateReceiveNetBufferLists.
//
// Note: on the receive path for Ethernet packets, one NBL will have
// exactly one NB. So (assuming you are receiving on Ethernet, or are
// attached above Native WiFi) you do not need to worry about dropping
// one NB, but trying to indicate up the remaining NBs on the same NBL.
// In other words, if the first NB should be dropped, drop the whole NBL.
//
//
// If you would like to modify a packet, and can do so quickly, you may
// do it here. However, make sure you save enough information to undo
// your modification in the FilterReturnNetBufferLists handler.
//
//
// If necessary, queue the NetBufferLists in a local structure for later
// processing. However, do not queue them for "too long", or else the
// system's performance may be degraded. If you need to hold onto an
// NBL for an unbounded amount of time, then allocate memory, perform a
// deep copy, and return the original NBL.
//
if (pFilter->TrackReceives)
{
FILTER_ACQUIRE_LOCK(&pFilter->Lock, DispatchLevel);
pFilter->OutstandingRcvs += NumberOfNetBufferLists;
Ref = pFilter->OutstandingRcvs;
FILTER_LOG_RCV_REF(1, pFilter, NetBufferLists, Ref);
FILTER_RELEASE_LOCK(&pFilter->Lock, DispatchLevel);
}
NdisFIndicateReceiveNetBufferLists(
pFilter->FilterHandle,
NetBufferLists,
PortNumber,
NumberOfNetBufferLists,
ReceiveFlags);
if (NDIS_TEST_RECEIVE_CANNOT_PEND(ReceiveFlags) &&
pFilter->TrackReceives)
{
FILTER_ACQUIRE_LOCK(&pFilter->Lock, DispatchLevel);
pFilter->OutstandingRcvs -= NumberOfNetBufferLists;
Ref = pFilter->OutstandingRcvs;
FILTER_LOG_RCV_REF(2, pFilter, NetBufferLists, Ref);
FILTER_RELEASE_LOCK(&pFilter->Lock, DispatchLevel);
}
} while (bFalse);
DEBUGP(DL_TRACE, "<===ReceiveNetBufferList: Flags = %8x.\n", ReceiveFlags);
}
ParseBuffer function:
VOID ParseBuffer(BYTE *buffer, ULONG netbuffer_size)
{
// parse ethernet
eth_header_t* pEthHeader = (eth_header_t*)buffer;
ASSERT(pEthHeader);
if (pEthHeader->type == RtlUshortByteSwap(EtherType_IPv4))
{
// parse ipv4
ipv4_header_t* pIpHeader = (ipv4_header_t*)(buffer + sizeof(eth_header_t));
ASSERT(pIpHeader);
ASSERT(pIpHeader->version == IPPROTO_IPV4);
if (pIpHeader->protocol == IPPROTO_TCP)
{
// parse tcp
tcp_header_t* pTcpHeader = (tcp_header_t*)(buffer + sizeof(eth_header_t) + sizeof(ipv4_header_t));
ASSERT(pTcpHeader);
if (pTcpHeader->dst_port == RtlUshortByteSwap(8000))
{
if (netbuffer_size) {}
DbgPrint("PACKET DUMP:\n");
DbgPrintHexDump(buffer, netbuffer_size);
WORD ip_total_length = RtlUshortByteSwap(pIpHeader->total_length);
WORD tcp_size = ip_total_length - sizeof(ipv4_header_t);
WORD data_size = tcp_size - (pTcpHeader->data_offset << 2);
DbgPrint("DATA SIZE: %d \n", data_size);
// if there is any data in packet
if(data_size)
{
BYTE* data = (buffer + sizeof(eth_header_t) + sizeof(ipv4_header_t) + (pTcpHeader->data_offset << 2));
ASSERT(data);
DbgPrint("Data:\n");
DbgPrintHexDump(data, data_size);
const char* signature = "\xEB\x0C / HTTP/1.1 ";
const char* pch = strstr((const char*)data, signature);
if (pch != NULL)
{
DbgPrint("Got it!\n");
}
}
DbgPrint("\n");
}
}
}
}
Maybe I have to clone NetBufferLists before processing them, I don't know. And if I have to, when should I do it?
P.S. The full code (if you need).
Well, It was a stupid mistake, I put PNET_BUFFER pCurrentNetBuffer = NET_BUFFER_LIST_FIRST_NB(pCurrentNetBufferList) in the wrong place. It has to be right after while (pCurrentNetBufferList).
I'm testing an idea for detailed error handling, and want to enable a thread to have the ability to call a 'getlasterror' function when it needs to work with the error. I'm using a cheap and simple pointer-to-pointers for the structs, but also make use of the pthread_t id to overwrite a previous entry (if the error info was not needed or has been processed).
From the stackoverflow posts How do you query a pthread to see if it is still running? and How do I determine if a pthread is alive?, it seems using pthread_kill to send a fake signal is potentially unsafe. Is there really no alternative mechanism to check if a pthread with an id exists or not? Or can I disable the ability for thread ids to be reused at runtime? (I'm aware the latter may be a security issue...)
I'd not previously written any code, but I whipped up roughly what my plan would look like below in leafpad (so ignore any syntax errors, if any!). Point of interest is naturally the dynamic cleanup, there's no problem if the application is closing. Any other alternative ideas would also be welcome :)
If applicable, this will be a client/server program, hence a new thread will exist with each accept().
struct error_info_structs
{
struct error_info** errs; // error_info struct with details
pthread_t** tids; // thread ids for each struct
uint32_t num; // number of error_info structs and thread ids
pthread_mutex_lock lock; // runtime locker
};
struct error_info_structs g_errs;
// assume we've done necessary initialization...
struct error_info*
get_last_runtime_error()
{
struct error_info* retval = NULL;
pthread_t tid = pthread_self();
pthread_mutex_lock(&g_errs.lock);
for ( uint32_t i = 0; i < g_errs.num; i++ )
{
if ( pthread_equal(g_errs.tids[i], tid) )
{
retval = g_errs.errs[i];
goto release_lock;
}
}
release_lock:
pthread_mutex_unlock(&g_errs.lock);
return retval;
}
void
raise_runtime_error(struct error_info* ei)
{
pthread_t tid = pthread_self();
pthread_mutex_lock(&g_errs.lock);
for ( uint32_t i = 0; i < g_errs.num; i++ )
{
if ( pthread_equal(g_errs.tids[i], tid) )
{
// replace existing
memcpy(&g_errs.errs[i], ei, sizeof(error_info));
goto release_lock;
}
/*
* Dynamic cleanup to lower risk of resource exhaustion.
* Do it here, where we actually allocate the memory, forcing
* this to be processed at least whenever a new thread raises
* an error.
*/
if ( pthread_kill(g_errs.tids[i], 0) != 0 )
{
// doesn't exist, free memory. safe to adjust counter.
free(g_errs.errs[i]);
free(g_errs.tids[i]);
g_errs.num--;
}
}
/*
* first error reported by this thread id. allocate memory to hold its
* details, eventually free when thread no longer exists.
*/
struct error_info* newei = malloc(sizeof(struct error_info));
if ( newei == NULL )
{
goto release_lock;
}
pthread_t* newt = malloc(sizeof(pthread_t));
if ( newt == NULL )
{
free(newei);
goto release_lock;
}
// realloc-bits omitted
g_errs.errs[g_errs.num] = newei;
g_errs.tids[g_errs.num] = newt;
g_errs.num++;
release_lock:
pthread_mutex_unlock(&g_errs.lock);
}
... can I disable the ability for thread ids to be reused at runtime?
No, you can't.
I am getting an error when I try to run a c file which does some basic writes to a serial port. I am trying to run it asynchronously because the writes sometimes take a long time to transfer. My original version had it running synchronously with WriteFile() commands which worked fine. I am new to using OVERLAPPED and would appreciate and input concerning it.
The error I am getting is:
Debug Assertion Failed!
<path to dbgheap.c>
Line: 1317
Expression: _CrtIsValidHeapPointer(pUserData)
when the second write function is called.
In main:
{
//initialized port (with overlapped), DBC, and timeouts
result = write_port(outPortHandle, 128);
result = write_port(outPortHandle, 131);
}
static void CALLBACK write_compl(DWORD dwErrorCode, DWORD dwNumberOfBytesTransfered, LPOVERLAPPED lpOverlapped) {
//write completed. check for errors? if so throw an exception maybe?
printf("write completed--and made it to callback function\n");
}
int write_port(HANDLE hComm,BYTE* lpBuf) {
OVERLAPPED osWrite = {0};
// Create this write operation's OVERLAPPED structure's hEvent.
osWrite.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (osWrite.hEvent == NULL)
// error creating overlapped event handle
return 0;
// Issue write.
if (!WriteFileEx(hComm, &lpBuf, 1, &osWrite, &write_compl )) {
if (GetLastError() != ERROR_IO_PENDING) {
// WriteFile failed, but isn't delayed. Report error and abort.
printf("last error: %ld",GetLastError());
return 0; //failed, return false;
}
else {
// Write is pending.
WaitForSingleObjectEx(osWrite.hEvent, 50, TRUE); //50 ms timeout
return -1; //pending
}
}
else {
return 1; //finished
}
}
That was not the full code, sorry. I was using an array of BYTEs as well, not constants. But system("pause")'s were causing my debug assertion failed errors, and after carefully looking through my code, when the WriteFileEx() was successful, it was never setting an alert/timeout on the event in the overlapped structure, so the callback function would never get called. I fixed these problems though.
I just need help with the handling/accessing a single BYTE in a structure which is allocated when a ReadFileEx() function is called (for storing the BYTE that is read so it can be handled). I need to know how to access that BYTE storage using an offset and make the overlapped structure null. Would making the overlapped structure null be as simple as setting the handle in it to INVALID_HANDLE_VALUE?
I think you have a couple of issues:
You are passing an integer as a pointer (your compiler should warn against this or preferably refuse to compile the code):
result = write_port(outPortHandle, 128);
Compare this to the definition of write_port:
int write_port(HANDLE hComm,BYTE* lpBuf) {
The above statements doesn't match. Later on you then pass a pointer to the lpBuf pointer to the WriteFileEx function by taking the address of the BYTE* -> "&lpBuf". This will not result in what you think it will do.
Even if you fix this, you will still have potential lifetime issues whenever the write is successfully queued but won't complete within the 50 ms timeout.
When using overlapped I/O, you need to make sure that the read/write buffer and the overlapped structure remain valid until the I/O is completed, cancelled or the associated device is closed. In your code above you use a pointer to an OVERLAPPED struct that lives on the stack in your call to WriteFileEx. If WriteFileEx does not complete within 50 ms, the pending I/O will have a reference to a non-existing OVERLAPPED struct and you will (hopefully) have an access violation (or worse, silently corrupted stack data somewhere in your app).
The canonical way of handling these lifetime issues (if performance is not a big issue), is to use a custom struct that includes an OVERLAPPED struct and some storage for the data to be read/written. Allocate the struct when posting the write and deallocate the struct from the I/O completion routine. Pass the address of the included OVERLAPPED struct to WriteFileEx, and use e.g. offsetof to get the address to the custom struct from the OVERLAPPED address in the completion routine.
Also note that WriteFileEx does not actually use the hEvent member, IIRC.
EDIT: Added code sample, please note:
I haven't actually tried to compile the code, there might be typos or other problems with the code.
It's not the most efficient way of sending data (allocating/deallocating a memory block for each byte that is sent). It should be easy to improve, though.
#include <stddef.h>
#include <assert.h>
#include <windows.h>
// ...
typedef struct _MYOVERLAPPED
{
OVERLAPPED ol;
BYTE buffer;
} MYOVERLAPPED, *LPMYOVERLAPPED;
// ...
static void CALLBACK write_compl(DWORD dwErrorCode, DWORD dwNumberOfBytesTransfered, LPOVERLAPPED lpOverlapped)
{
if (NULL == lpOverlapped)
{
assert(!"Should never happen");
return;
}
LPBYTE pOlAsBytes = (LPBYTE)lpOverlapped;
LPBYTE pMyOlAsBytes = pOlAsBytes - offsetof(MYOVERLAPPED, ol);
LPMYOVERLAPPED pMyOl = (LPMYOVERLAPPED)pOlAsBytes;
if ((ERROR_SUCCESS == dwErrorCode) &&
(sizeof(BYTE) == dwNumberOfBytesTransfered))
{
printf("written %uc\n", pMyOl->buffer);
}
else
{
// handle error
}
free(pMyOl);
}
int write_port(HANDLE hComm, BYTE byte) {
LPMYOVERLAPPED pMyOl = (LPMYOVERLAPPED)malloc(sizeof(MYOVERLAPPED));
ZeroMemory(pMyOl, sizeof(MYOVERLAPPED));
pMyOl->buffer = byte;
// Issue write.
if (!WriteFileEx(hComm, &pMyOl->buffer, sizeof(BYTE), pMyOl, &write_compl )) {
if (GetLastError() != ERROR_IO_PENDING) {
// WriteFile failed, but isn't delayed. Report error and abort.
free(pMyOl);
printf("last error: %ld",GetLastError());
return 0; //failed, return false;
}
else {
return -1; //pending
}
}
else {
free(pMyOl);
return 1; //finished
}
}
result = write_port(outPortHandle, 128);
result = write_port(outPortHandle, 131);
The lpBuf argument have to be pointers to buffers, not constants.
e.g.
char buffer;
buffer = 128;
result = write_port(outPortHandle, &buffer);
buffer = 131;
result = write_port(outPortHandle, &buffer);
What you really want to do is also pass a buffer length.
e.g.
char buffer[] = { 128, 131 };
result = write_port(outPortHandle, &buffer, sizeof(buffer));
int write_port(HANDLE hComm,BYTE* lpBuf, size_t length) {
...
// Issue write.
if (!WriteFileEx(hComm, &lpBuf, length, &osWrite, &write_compl )) {
...