App is crashing after trying to free reassigned pointer
- Dominant language
- Dart
- Stars
- 275
- Forks
- 144
- Avg merge
- 2d 10h
- Merged PRs (30d)
- 47
Description
This is the place where crash happens, if you remove this line then there will be no crash. And if this pointer is not freed then it leads to a huge memory leak
```Dart
// CRASH IS HAPPENING HERE:
calloc.free(buffer);
```
```yaml
ffi: ^2.1.0
win32: ^5.0.7
```
```Dart
import 'dart:ffi';
import 'package:ffi/ffi.dart';
import 'package:flutter/material.dart';
import 'package:win32/win32.dart';
/// Loops through all system processes with [NtQuerySystemInformation].
Future loopThroughSystemProcessesWithNtApi(Function callback) async {
final Pointer dwSize = calloc();
Pointer buffer = calloc();
// SystemProcessInformation = 5;
int info = dNtQuerySystemInformation(5, nullptr, 0, dwSize);
// [STATUS_INFO_LENGTH_MISMATCH] means that we've got the size and now need
// to call this method once again but with actual pointer to the buffer
if (!ntSuccess(info) && info == statusInfoLengthMismatch) {
calloc.free(buffer);
buffer = calloc(dwSize.value);
info = dNtQuerySystemInformation(5, buffer, dwSize.value, dwSize);
}
// Failed to retrieve processes list
if (!ntSuccess(info)) {
calloc.free(dwSize);
calloc.free(buffer);
return false;
}
// Loop through all found entities
while (buffer.ref.nextEntryOffset != 0) {
try {
// Stop looping if we found what we've been looking for
final bool result = await callback.call(buffer) as bool;
if (result) break;
// Stop updating the loop to avoid crashes
if (buffer.ref.nextEntryOffset == 0) break;
// Update buffer to the next process
buffer = buffer
.cast()
.elementAt(buffer.ref.nextEntryOffset)
.cast();
} catch (_) {
break;
}
}
// CRASH IS HAPPENING HERE:
calloc.free(buffer);
calloc.free(dwSize);
return true;
}
/// Macros for NTSTATUS.
const int statusInfoLengthMismatch = -1073741820;
// Shortcut for Pointer
typedef PULONG = Pointer;
/// Checks if Nt... function has succeded.
bool ntSuccess(int result) => result >= 0;
// Allocalte ntdll API
final Pointer _ntdllTitle = 'ntdll.dll'.toNativeUtf16();
final int _ntdllHndle = GetModuleHandle(_ntdllTitle);
/// Dart representation of native [NtQuerySystemInformation] function.
/// ```c
/// [in] SYSTEM_INFORMATION_CLASS SystemInformationClass,
/// [in, out] PVOID SystemInformation,
/// [in] ULONG SystemInformationLength,
/// [out, optional] PULONG ReturnLength
/// ```
int Function(int, Pointer, int, PULONG)?
_pNtQuerySystemInformationFunc;
/// [NtQuerySystemInformation may be altered or unavailable in future versions
/// of Windows. Applications should use the alternate functions
/// listed in this topic.] Retrieves the specified system information.
int dNtQuerySystemInformation(
int dwFlag,
Pointer buffer,
int dwSize,
PULONG out,
) {
if (_pNtQuerySystemInformationFunc == null) {
// Find function address with
final Pointer ansi = 'NtQuerySystemInformation'.toANSI();
final Pointer pNtQuerySystemInformation = GetProcAddress(_ntdllHndle, ansi);
calloc.free(ansi);
// Return 0 if function was not found
if (pNtQuerySystemInformation == nullptr) return 0;
_pNtQuerySystemInformationFunc = pNtQuerySystemInformation
.cast>()
.asFunction();
}
return _pNtQuerySystemInformationFunc?.call(dwFlag, buffer, dwSize, out) ?? 0;
}
/// Basic info about the process
sealed class SystemProcessInformation extends Struct {
@Uint32()
external int nextEntryOffset;
@Uint32()
external int numberOfThreads;
external LargeInteger workingSetPrivateSize;
@Uint32()
external int hardFaultCount;
@Uint32()
external int numberOfThreadsHighWatermark;
@Uint64()
external int cycleTime;
external LargeInteger createTime;
external LargeInteger userTime;
external LargeInteger kernelTime;
external UnicodeString imageName;
@Int32()
external int basePriority;
@IntPtr()
external int uniqueProcessId;
@IntPtr()
external int inheritedFromUniqueProcessId;
@Uint32()
external int handleCount;
@Uint32()
external int sessionId;
@IntPtr()
external int uniqueProcessKey;
@IntPtr()
external int peakVirtualSize;
@IntPtr()
external int virtualSize;
@Uint32()
external int pageFaultCount;
@IntPtr()
external int peakWorkingSetSize;
@IntPtr()
external int workingSetSize;
@IntPtr()
external int quotaPeakPagedPoolUsage;
@IntPtr()
external int quotaPagedPoolUsage;
@IntPtr()
external int quotaPeakNonPagedPoolUsage;
@IntPtr()
external int quotaNonPagedPoolUsage;
@IntPtr()
external int pagefileUsage;
@IntPtr()
external int peakPagefileUsage;
@IntPtr()
external int ppivatePageCount;
external LargeInteger readOperationCount;
external LargeInteger writeOperationCount;
external LargeInteger otherOperationCount;
external LargeInteger readTransferCount;
external LargeInteger writeTransferCount;
external LargeInteger otherTransferCount;
@Array(1)
external Array threads;
}
/// Represents a 64-bit signed integer value.
sealed class LargeInteger extends Union {
external _LargeIntegerFirst firstStruct;
external _LargeIntegerSecond secondStruct;
@Int64()
external int quadPart;
}
/// Structure for LargeInteger union.
sealed class _LargeIntegerFirst extends Struct {
@Uint32()
external int lowPart;
@Int32()
external int highPart;
}
/// Structure for LargeInteger union.
sealed class _LargeIntegerSecond extends Struct {
@Uint32()
external int lowPart;
@Int32()
external int highPart;
}
/// The SYSTEM_THREAD_INFORMATION structure contains information about
/// a thread running on a system.
sealed class SystemThreadInformation extends Struct {
external LargeInteger kernelTime;
external LargeInteger userTime;
external LargeInteger createTime;
@Uint32()
external int waitTime;
external Pointer startAddress;
external ClientID clientId;
@Int32()
external int priority;
@Int32()
external int basePriority;
@Uint32()
external int contextSwitches;
@Uint32()
external int threadState;
@Uint32()
external int waitReason;
}
/// The UNICODE_STRING structure is used to define Unicode strings.
sealed class ClientID extends Struct {
@IntPtr()
external int uniqueProcess;
@IntPtr()
external int uniqueThread;
}
/// The UNICODE_STRING structure is used to define Unicode strings.
sealed class UnicodeString extends Struct {
@Uint16()
external int length;
@Uint16()
external int maximumLength;
external Pointer buffer;
}
void main() {
loopThroughSystemProcessesWithNtApi(
(Pointer info) {
print('123');
return false;
},
);
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
// This is the theme of your application.
//
// TRY THIS: Try running your application with "flutter run". You'll see
// the application has a blue toolbar. Then, without quitting the app,
// try changing the seedColor in the colorScheme below to Colors.green
// and then invoke "hot reload" (save your changes or press the "hot
// reload" button in a Flutter-supported IDE, or press "r" if you used
// the command line to start the app).
//
// Notice that the counter didn't reset back to zero; the application
// state is not lost during the reload. To reset the state, use hot
// restart instead.
//
// This works for code too, not just values: Most code changes can be
// tested with just a hot reload.
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
// This widget is the home page of your application. It is stateful, meaning
// that it has a State object (defined below) that contains fields that affect
// how it looks.
// This class is the configuration for the state. It holds the values (in this
// case the title) provided by the parent (in this case the App widget) and
// used by the build method of the State. Fields in a Widget subclass are
// always marked "final".
final String title;
@override
State createState() => _MyHomePageState();
}
class _MyHomePageState extends State {
int _counter = 0;
void _incrementCounter() {
setState(() {
// This call to setState tells the Flutter framework that something has
// changed in this State, which causes it to rerun the build method below
// so that the display can reflect the updated values. If we changed
// _counter without calling setState(), then the build method would not be
// called again, and so nothing would appear to happen.
_counter++;
});
}
@override
Widget build(BuildContext context) {
// This method is rerun every time setState is called, for instance as done
// by the _incrementCounter method above.
//
// The Flutter framework has been optimized to make rerunning build methods
// fast, so that you can just rebuild anything that needs updating rather
// than having to individually change instances of widgets.
return Scaffold(
appBar: AppBar(
// TRY THIS: Try changing the color here to a specific color (to
// Colors.amber, perhaps?) and trigger a hot reload to see the AppBar
// change color while the other colors stay the same.
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
// Here we take the value from the MyHomePage object that was created by
// the App.build method, and use it to set our appbar title.
title: Text(widget.title),
),
body: Center(
// Center is a layout widget. It takes a single child and positions it
// in the middle of the parent.
child: Column(
// Column is also a layout widget. It takes a list of children and
// arranges them vertically. By default, it sizes itself to fit its
// children horizontally, and tries to be as tall as its parent.
//
// Column has various properties to control how it sizes itself and
// how it positions its children. Here we use mainAxisAlignment to
// center the children vertically; the main axis here is the vertical
// axis because Columns are vertical (the cross axis would be
// horizontal).
//
// TRY THIS: Invoke "debug painting" (choose the "Toggle Debug Paint"
// action in the IDE, or press "p" in the console), to see the
// wireframe for each widget.
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headlineMedium,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
```
Contributor guide
Assessment
This issue has not been assessed yet.