jjzjj

c++ - 读取内存时访问冲突

coder 2024-06-10 原文

背景

我正在使用 Visual Studio 2013 Community Edition 编写命令行 C++ 程序。它通过 LDAP 连接到 Active Directory 服务器,并检索几个属性(例如:办公地点、部门)中的唯一值列表。

问题

程序编译正常,但在运行时遇到内存访问问题:

LdapSearchResultTest1.exe 中 0x74EDC6B1 (Wldap32.dll) 处的未处理异常:0xC0000005:访问冲突读取位置 0xCCCCCCCC。

这是我第一次将 C++ 与外部库一起使用,所以我什至不确定如何调试它(通常我为 Android 编写 Java)。我花了一天的大部分时间环顾 SO 并根据对类似问题的答案尝试想法,但我仍然无法弄清楚。

确切的问题在于此函数调用中的最后一个参数:

// Do the search
int searchReturnCode = ldap_search_s(
    ldapSession,
    &searchBase[0],
    LDAP_SCOPE_SUBTREE,
    filter,
    pAttributes,
    0,
    &pSearchResults); // Error is here

我的代码

我的代码基于 MSDN 网站上的示例,我在我的代码之后复制了该示例。这是一个 SSCCE证明了这个问题:

#include<iostream>
#include<Windows.h>
#include<Winldap.h>
#include<WinBer.h>
#include<string>
#include<vector>
#include<algorithm>
using namespace std;

// Function headers
vector<string> get_unique_departments(LDAP*, char*, string);
vector<string> get_unique_office_locations(LDAP*, char*, string);
vector<string> get_unique_values_by_property(LDAP*, char*, string, string);
void print_list(string, vector<string>);

// Main function
int main(int argc, char* argv[]) {

    /**
     * Take AD connection arguments from Windows command line:
     * - Server address
     * - Server port
     * - Username
     * - Password
     * - Search base(s) (space separated if there are multiple)
     *
     * Example call from Windows command line:
     * > program.exe ad-test.example.com 389 joe@ad-test.example.com L3tM31n "OU=Development Team,DC=ad-test,DC=example,DC=com" "OU=Management Team,DC=ad-test,DC=example,DC=com"
     */
    string serverAddress = argv[1];
    int serverPort = atoi(argv[2]);
    string username = argv[3];
    string password = argv[4];
    vector<string> searchBases;
    for (int i = 0; i < argc; i++) {
        searchBases.push_back(argv[i]);
    }

    // If debug build, print received parameters
    #ifdef _DEBUG
    cout << "Server address: " << serverAddress << endl
        << "Server port: " << serverPort << endl
        << "Username: " << username << endl
        << "Password: " << password << endl;
    for (size_t i = 5; i < searchBases.size(); i++) {
        cout << "Search base: " << searchBases.at(i) << endl;
    }
    cout << endl;
    #endif

    // Initiate LDAP connection to Active Directory
    int ldapVersion = LDAP_VERSION3;
    LDAP* ldapSession = ldap_init(&serverAddress[0], serverPort);
    ldap_set_option(ldapSession, LDAP_OPT_PROTOCOL_VERSION, &ldapVersion);
    ULONG ldapConnection = ldap_connect(ldapSession, nullptr);

    // Bind user
    int ldapBindResult = ldap_simple_bind_s(ldapSession, &username[0], &password[0]);
    if (ldapBindResult != LDAP_SUCCESS) {
        ldap_unbind(ldapSession);
        #ifdef _DEBUG
        cout << "Unable to connect to LDAP directory" << endl << endl;
        #endif
    }
    else {
        #ifdef _DEBUG
        cout << "Connected to LDAP!" << endl << endl;
        #endif
    }

    // The LDAP object filter
    char* filter = "(&(objectCategory=person)(objectClass=user))";

    // Get lists of departments and offices
    vector<string> departments, offices;
    for (int i = 0; i < searchBases.size(); i++) {
        vector<string> tempDepts = get_unique_departments(ldapSession, filter, searchBases.at(i));
        vector<string> tempOffices = get_unique_office_locations(ldapSession, filter, searchBases.at(i));
        for (int j = 0; j < tempDepts.size(); j++) {
            departments.push_back(tempDepts.at(j));
        }
        for (int j = 0; j < tempOffices.size(); j++) {
            offices.push_back(tempOffices.at(j));
        }
    }

    // Print the lists
    print_list("Departments", departments);
    print_list("Offices", offices);

    // Return
    return 0;

}

// Retrieve a list of unique departments
vector<string> get_unique_departments(LDAP* session, char* filter, string searchBase) {
    return get_unique_values_by_property(session, filter, searchBase, "department");
}

// Retrieve a list of unique office locations
vector<string> get_unique_office_locations(LDAP* session, char* filter, string searchBase) {
    return get_unique_values_by_property(session, filter, searchBase, "office");
}

// Get a list of an attribute's unique values
vector<string> get_unique_values_by_property(LDAP* ldapSession, char* filter, string searchBase, string property) {

    // Initialize some variables
    vector<string> results;
    char* pAttributes[1];
    pAttributes[0] = &property[0];
    LDAPMessage* pSearchResults = NULL;
    int numResults = 0;

    // Do the search
    int searchReturnCode = ldap_search_s(
        ldapSession,
        &searchBase[0],
        LDAP_SCOPE_SUBTREE,
        filter,
        pAttributes,
        0,
        &pSearchResults); // Error is here

    // Process results
    if (searchReturnCode == LDAP_SUCCESS) {

        // Initialize some variables
        LDAPMessage* pEntry = NULL;
        char* pEntryDN = NULL;
        char* sMsg;
        BerElement* pBer = NULL;
        char* pAttribute = NULL;
        char** ppValue = NULL;
        ULONG iValue = 0;

        // Count the results
        numResults = ldap_count_entries(ldapSession, pSearchResults);

        // Loop over results
        for (ULONG i = 0; i < numResults; i++) {

            // Get the first/next entry
            if (!i) {
                pEntry = ldap_first_entry(ldapSession, pSearchResults);
            }
            else {
                pEntry = ldap_next_entry(ldapSession, pEntry);
            }

            // Fail if unable to get entry
            if (pEntry == NULL) {
                results.clear();
                return results;
            }

            // Loop over the attributes
            pAttribute = ldap_first_attribute(ldapSession, pEntry, &pBer);
            while (pAttribute != NULL) {

                // Get and handle the values
                ppValue = ldap_get_values(ldapSession, pEntry, pAttribute);
                if (ppValue != NULL) {
                    iValue = ldap_count_values(ppValue);
                    if (find(results.begin(), results.end(), *ppValue) == results.end()) {
                        results.push_back(*ppValue);
                    }

                    // Memory management
                    ldap_value_free(ppValue);
                    ppValue = NULL;
                    ldap_memfree(pAttribute);

                    // Get the next attribute
                    pAttribute = ldap_next_attribute(ldapSession, pEntry, pBer);
                }

            }

            // Memory management
            if (pBer != NULL) {
                ber_free(pBer, 0);
            }
            pBer = NULL;

        }

    }

    // Free search result memory
    if (pSearchResults != NULL) {
        ldap_msgfree(pSearchResults);
    }

    // Return
    return results;

}

// Print a vector-based list w/ header
void print_list(string header, vector<string> items) {
    if (items.size() > 0) {
        cout << header << ":" << endl;
        for (int i = 0; i < items.size(); i++) {
            cout << items.at(i) << endl;
        }
        cout << endl;
    }
}

MSDN 示例:搜索 LDAP 目录

来自 https://msdn.microsoft.com/en-us/library/aa367016%28v=vs.85%29.aspx

//----------------------------------------------
// Performing an LDAP Synchronous Search.
//
// Be aware that you must set the command prompt screen buffer 
// height to 350 and the width to 90.
//-------------------------------------------------------------

#include <windows.h>
#include <winldap.h>
#include <winber.h>
#include <rpc.h>
#include <rpcdce.h>
#include <stdio.h>

//-----------------------------------------------------------
// This subroutine must have validated credentials (name and
// password) passed to it.
//-----------------------------------------------------------
int MyLDAPSearch(PCHAR pUserName, PCHAR pPassword)
{
    //---------------------------------------------------------
    // Initialize a session. LDAP_PORT is the default port, 389.
    //---------------------------------------------------------
    PCHAR hostName = "fabrikam.com";
    LDAP* pLdapConnection = NULL;

    pLdapConnection = ldap_init(hostName, LDAP_PORT);

    if (pLdapConnection == NULL)
    {
        printf("ldap_init failed with 0x%x.\n",LdapGetLastError());
        ldap_unbind(pLdapConnection);
        return -1;
    }
    else
        printf("ldap_init succeeded \n");


    //-------------------------------------------------------
    // Set session options.
    //-------------------------------------------------------
    ULONG version = LDAP_VERSION3;
    ULONG numReturns = 10;
    ULONG lRtn = 0;

    // Set the version to 3.0 (default is 2.0).
    lRtn = ldap_set_option(
                    pLdapConnection,           // Session handle
                    LDAP_OPT_PROTOCOL_VERSION, // Option
                    (void*) &version);         // Option value

    if(lRtn == LDAP_SUCCESS)
        printf("ldap version set to 3.0 \n");
    else
    {
        printf("SetOption Error:%0lX\n", lRtn);
        ldap_unbind(pLdapConnection);
        return -1;
    }

    // Set the limit on the number of entries returned to 10.
    lRtn = ldap_set_option(
                    pLdapConnection,       // Session handle
                    LDAP_OPT_SIZELIMIT,    // Option
                    (void*) &numReturns);  // Option value

    if(lRtn == LDAP_SUCCESS)
        printf("Max return entries set to 10 \n");
    else
    {
        printf("SetOption Error:%0lX\n", lRtn);
        ldap_unbind(pLdapConnection);
        return -1;
    }


    //--------------------------------------------------------
    // Connect to the server.
    //--------------------------------------------------------

    lRtn = ldap_connect(pLdapConnection, NULL);

    if(lRtn == LDAP_SUCCESS)
        printf("ldap_connect succeeded \n");
    else
    {
        printf("ldap_connect failed with 0x%lx.\n",lRtn);
        ldap_unbind(pLdapConnection);
        return -1;
    }


    //--------------------------------------------------------
    // Bind with credentials.
    //--------------------------------------------------------
    PCHAR pMyDN = "DC=fabrikam,DC=com";
    SEC_WINNT_AUTH_IDENTITY secIdent;

    secIdent.User = (unsigned char*)pUserName;
    secIdent.UserLength = strlen(pUserName);
    secIdent.Password = (unsigned char*)pPassword;
    secIdent.PasswordLength = strlen(pPassword);
    secIdent.Domain = (unsigned char*)hostName;
    secIdent.DomainLength = strlen(hostName);
    secIdent.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI;

    lRtn = ldap_bind_s(
                pLdapConnection,      // Session Handle
                pMyDN,                // Domain DN
                (PCHAR)&secIdent,     // Credential structure
                LDAP_AUTH_NEGOTIATE); // Auth mode
    if(lRtn == LDAP_SUCCESS)
    {
        printf("ldap_bind_s succeeded \n");
        secIdent.Password = NULL; // Remove password pointer
        pPassword = NULL;         // Remove password pointer
    }
    else
    {
        printf("ldap_bind_s failed with 0x%lx.\n",lRtn);
        ldap_unbind(pLdapConnection);
        return -1;
    }

    //----------------------------------------------------------
    // Perform a synchronous search of fabrikam.com for 
    // all user objects that have a "person" category.
    //----------------------------------------------------------
    ULONG errorCode = LDAP_SUCCESS;
    LDAPMessage* pSearchResult;
    PCHAR pMyFilter = "(&(objectCategory=person)(objectClass=user))";
    PCHAR pMyAttributes[6];

    pMyAttributes[0] = "cn";
    pMyAttributes[1] = "company";
    pMyAttributes[2] = "department";
    pMyAttributes[3] = "telephoneNumber";
    pMyAttributes[4] = "memberOf";
    pMyAttributes[5] = NULL;

    errorCode = ldap_search_s(
                    pLdapConnection,    // Session handle
                    pMyDN,              // DN to start search
                    LDAP_SCOPE_SUBTREE, // Scope
                    pMyFilter,          // Filter
                    pMyAttributes,      // Retrieve list of attributes
                    0,                  // Get both attributes and values
                    &pSearchResult);    // [out] Search results

    if (errorCode != LDAP_SUCCESS)
    {
        printf("ldap_search_s failed with 0x%0lx \n",errorCode);
        ldap_unbind_s(pLdapConnection);
        if(pSearchResult != NULL)
            ldap_msgfree(pSearchResult);
        return -1;
    }
    else
        printf("ldap_search succeeded \n");

    //----------------------------------------------------------
    // Get the number of entries returned.
    //----------------------------------------------------------
    ULONG numberOfEntries;

    numberOfEntries = ldap_count_entries(
                        pLdapConnection,    // Session handle
                        pSearchResult);     // Search result

    if(numberOfEntries == NULL)
    {
        printf("ldap_count_entries failed with 0x%0lx \n",errorCode);
        ldap_unbind_s(pLdapConnection);
        if(pSearchResult != NULL)
            ldap_msgfree(pSearchResult);
        return -1;
    }
    else
        printf("ldap_count_entries succeeded \n");

    printf("The number of entries is: %d \n", numberOfEntries);


    //----------------------------------------------------------
    // Loop through the search entries, get, and output the
    // requested list of attributes and values.
    //----------------------------------------------------------
    LDAPMessage* pEntry = NULL;
    PCHAR pEntryDN = NULL;
    ULONG iCnt = 0;
    char* sMsg;
    BerElement* pBer = NULL;
    PCHAR pAttribute = NULL;
    PCHAR* ppValue = NULL;
    ULONG iValue = 0;

    for( iCnt=0; iCnt < numberOfEntries; iCnt++ )
    {
        // Get the first/next entry.
        if( !iCnt )
            pEntry = ldap_first_entry(pLdapConnection, pSearchResult);
        else
            pEntry = ldap_next_entry(pLdapConnection, pEntry);

        // Output a status message.
        sMsg = (!iCnt ? "ldap_first_entry" : "ldap_next_entry");
        if( pEntry == NULL )
        {
            printf("%s failed with 0x%0lx \n", sMsg, LdapGetLastError());
            ldap_unbind_s(pLdapConnection);
            ldap_msgfree(pSearchResult);
            return -1;
        }
        else
            printf("%s succeeded\n",sMsg);

        // Output the entry number.
        printf("ENTRY NUMBER %i \n", iCnt);

        // Get the first attribute name.
        pAttribute = ldap_first_attribute(
                      pLdapConnection,   // Session handle
                      pEntry,            // Current entry
                      &pBer);            // [out] Current BerElement

        // Output the attribute names for the current object
        // and output values.
        while(pAttribute != NULL)
        {
            // Output the attribute name.
            printf("     ATTR: %s",pAttribute);

            // Get the string values.

            ppValue = ldap_get_values(
                          pLdapConnection,  // Session Handle
                          pEntry,           // Current entry
                          pAttribute);      // Current attribute

            // Print status if no values are returned (NULL ptr)
            if(ppValue == NULL)
            {
                printf(": [NO ATTRIBUTE VALUE RETURNED]");
            }

            // Output the attribute values
            else
            {
                iValue = ldap_count_values(ppValue);
                if(!iValue)
                {
                    printf(": [BAD VALUE LIST]");
                }
                else
                {
                    // Output the first attribute value
                    printf(": %s", *ppValue);

                    // Output more values if available
                    ULONG z;
                    for(z=1; z<iValue; z++)
                    {
                        printf(", %s", ppValue[z]);
                    }
                }
            } 

            // Free memory.
            if(ppValue != NULL)  
                ldap_value_free(ppValue);
            ppValue = NULL;
            ldap_memfree(pAttribute);

            // Get next attribute name.
            pAttribute = ldap_next_attribute(
                            pLdapConnection,   // Session Handle
                            pEntry,            // Current entry
                            pBer);             // Current BerElement
            printf("\n");
        }

        if( pBer != NULL )
            ber_free(pBer,0);
        pBer = NULL;
    }

    //----------------------------------------------------------
    // Normal cleanup and exit.
    //----------------------------------------------------------
    ldap_unbind(pLdapConnection);
    ldap_msgfree(pSearchResult);
    ldap_value_free(ppValue);
    return 0;

}

最佳答案

阅读 ldap_search_s 的文档,我看到:

base [in]

Pointer to a null-terminated string that contains the distinguished name of the entry at which to start the search.

但是,&searchBase[0] 只会为您提供一个指向存储空间的指针,std::string 使用 - 不需要以 null 终止。您应该改用 searchBase.c_str(),因为这可以保证为您提供一个空终止的 c 字符串。

关于c++ - 读取内存时访问冲突,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30337642/

有关c++ - 读取内存时访问冲突的更多相关文章

  1. ruby - 为什么我可以在 Ruby 中使用 Object#send 访问私有(private)/ protected 方法? - 2

    类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc

  2. ruby-on-rails - Ruby net/ldap 模块中的内存泄漏 - 2

    作为我的Rails应用程序的一部分,我编写了一个小导入程序,它从我们的LDAP系统中吸取数据并将其塞入一个用户表中。不幸的是,与LDAP相关的代码在遍历我们的32K用户时泄漏了大量内存,我一直无法弄清楚如何解决这个问题。这个问题似乎在某种程度上与LDAP库有关,因为当我删除对LDAP内容的调用时,内存使用情况会很好地稳定下来。此外,不断增加的对象是Net::BER::BerIdentifiedString和Net::BER::BerIdentifiedArray,它们都是LDAP库的一部分。当我运行导入时,内存使用量最终达到超过1GB的峰值。如果问题存在,我需要找到一些方法来更正我的代

  3. ruby - 如何将脚本文件的末尾读取为数据文件(Perl 或任何其他语言) - 2

    我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚

  4. ruby-on-rails - 在混合/模块中覆盖模型的属性访问器 - 2

    我有一个包含模块的模型。我想在模块中覆盖模型的访问器方法。例如:classBlah这显然行不通。有什么想法可以实现吗? 最佳答案 您的代码看起来是正确的。我们正在毫无困难地使用这个确切的模式。如果我没记错的话,Rails使用#method_missing作为属性setter,因此您的模块将优先,阻止ActiveRecord的setter。如果您正在使用ActiveSupport::Concern(参见thisblogpost),那么您的实例方法需要进入一个特殊的模块:classBlah

  5. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

    我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

  6. Ruby 写入和读取对象到文件 - 2

    好的,所以我的目标是轻松地将一些数据保存到磁盘以备后用。您如何简单地写入然后读取一个对象?所以如果我有一个简单的类classCattr_accessor:a,:bdefinitialize(a,b)@a,@b=a,bendend所以如果我从中非常快地制作一个objobj=C.new("foo","bar")#justgaveitsomerandomvalues然后我可以把它变成一个kindaidstring=obj.to_s#whichreturns""我终于可以将此字符串打印到文件或其他内容中。我的问题是,我该如何再次将这个id变回一个对象?我知道我可以自己挑选信息并制作一个接受该信

  7. ruby - 续集在添加关联时访问many_to_many连接表 - 2

    我正在使用Sequel构建一个愿望list系统。我有一个wishlists和itemstable和一个items_wishlists连接表(该名称是续集选择的名称)。items_wishlists表还有一个用于facebookid的额外列(因此我可以存储opengraph操作),这是一个NOTNULL列。我还有Wishlist和Item具有续集many_to_many关联的模型已建立。Wishlist类也有:selectmany_to_many关联的选项设置为select:[:items.*,:items_wishlists__facebook_action_id].有没有一种方法可以

  8. ruby-on-rails - Ruby 中的内存模型 - 2

    ruby如何管理内存。例如:如果我们在执行过程中采用C程序,则以下是内存模型。类似于这个ruby如何处理内存。C:__________________|||stack|||------------------||||------------------|||||Heap|||||__________________|||data|__________________|text|__________________Ruby:? 最佳答案 Ruby中没有“内存”这样的东西。Class#allocate分配一个对象并返回该对象。这就是程序

  9. 世界前沿3D开发引擎HOOPS全面讲解——集3D数据读取、3D图形渲染、3D数据发布于一体的全新3D应用开发工具 - 2

    无论您是想搭建桌面端、WEB端或者移动端APP应用,HOOPSPlatform组件都可以为您提供弹性的3D集成架构,同时,由工业领域3D技术专家组成的HOOPS技术团队也能为您提供技术支持服务。如果您的客户期望有一种在多个平台(桌面/WEB/APP,而且某些客户端是“瘦”客户端)快速、方便地将数据接入到3D应用系统的解决方案,并且当访问数据时,在各个平台上的性能和用户体验保持一致,HOOPSPlatform将帮助您完成。利用HOOPSPlatform,您可以开发在任何环境下的3D基础应用架构。HOOPSPlatform可以帮您打造3D创新型产品,HOOPSSDK包含的技术有:快速且准确的CAD

  10. ruby - 使用 `+=` 和 `send` 方法 - 2

    如何将send与+=一起使用?a=20;a.send"+=",10undefinedmethod`+='for20:Fixnuma=20;a+=10=>30 最佳答案 恐怕你不能。+=不是方法,而是语法糖。参见http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_expressions.html它说Incommonwithmanyotherlanguages,Rubyhasasyntacticshortcut:a=a+2maybewrittenasa+=2.你能做的最好的事情是:

随机推荐